#include <winsock2.h>
#include "MySQL/mysql.h"
#include <math.h>
#include <time.h>
#include <sys/timeb.h>

#include "CSemaphore.h"
#include "Utils/T2List.h"
#include "Utils/T2AvlList.h"
#include "Utils/listKeyClass.h"
#include "CChar.h"
#include "CLinkshell.h"
#include "CPacket.h"
#include "CActionPacket.h"
#include "CGlobal.h"
#include "CLuaManager.h"
#include "CUtil.h"
#include "CNpc.h"
#include "CDb.h"
#include "CBlowfish.h"
#include "CCharJobInfoPacket.h"
#include "CDownloadingDataPacket.h"
#include "CDownloadingAppearancePacket.h"
#include "CDownloadingUnknownPacket.h"
#include "CZoneInPacket.h"
#include "CBazaarMessagePacket.h"
#include "CCharStatsPacket.h"
#include "CCharSkillPacket.h"
#include "CCheckPacket.h"
#include "CEquipPacket.h"
#include "CEventPacket.h"
#include "CKneelAnimationPacket.h"
#include "CInventoryAssignPacket.h"
#include "CInventoryModifyPacket.h"
#include "CInventoryFinishPacket.h"
#include "CInventoryItemPacket.h"
#include "CInventorySizePacket.h"
#include "CModifier.h"
#include "CItemContainer.h"
#include "CGuildShopHandler.h"
#include "CGuildBuyMenuPacket.h"
#include "CGuildBuyMessagePacket.h"
#include "CGuildSellMenuPacket.h"
#include "CGuildSellMessagePacket.h"
#include "CLockOnPacket.h"
#include "CNpcPacket.h"
#include "CMessagePacket.h"
#include "CMessagePacketTwo.h"
#include "CMogMenuPacket.h"
#include "CNpcTextPacket.h"
#include "CQuestMissionLogPacket.h"
#include "CReleasePacket.h"
#include "CServerMessagePacket.h"
#include "CShopBuyPacket.h"
#include "CShopValuePacket.h"
#include "CSixtySevenPacket.h"
#include "CCharPacket.h"
#include "CSpawnPointHandler.h"
#include "CSpecialMessagePacket.h"
#include "CStandardMessagePacket.h"
#include "CStatusEffectHandler.h"
#include "CSynthResultPacket.h"
#include "CSynthResultShowPacket.h"
#include "CTradeHandler.h"
#include "CTradeActionPacket.h"
#include "CUpdateCharPacket.h"
#include "CUpdateKeyItemsPacket.h"
#include "CUpdateSpellsPacket.h"
#include "CUpdateAbilitiesPacket.h" 
#include "CPositionPacket.h"
#include "CTransportHandler.h"
#include "CPartyInvitePacket.h"
#include "CCharHealthPacket.h"
#include "CUpdatePartyPacket.h"
#include "CDefinePartyPacket.h"
#include "CPartyMapPacket.h"
#include "CWeatherPacket.h"
#include "CFindItemPacket.h"
#include "CMobHandler.h"
#include "CRaiseTractorMenuPacket.h"
#include <vector>
//CChar CChar::first;
int charnum=0;

CChar::CChar() 
{
	packetList = new T3ListProtectedDebug<CStringKeyCS, CBasicPacket*, T2AVLList>();
	m_logoutType = 0;

	attributeSemaphore=new CSemaphore(1);
	commListSemaphore=new CSemaphore(1);
	recastSpellListSemaphore=new CSemaphore(1);
	recastAbilityListSemaphore=new CSemaphore(1);

	newSpawnIdList = new T3ListProtectedDebug<int, char, T2AVLList>();
	hasEnmityList = new T3ListProtectedDebug<unsigned int, CBaseEntity*,T2AVLList>();

	gil=new CItem;

	CConsole::outDebOnly("Chars++: %d",++charnum);
	this->m_zoneHack = 0;

	loggedIn = false;
	loggedOut = false;
	inEvent = false;
	lsLoginMessageSent = false;

	memset(&clientConn,0,sizeof(connections));

	this->m_levelCap	= 1;

	this->m_jobMain		= 0;
	this->m_lvlMain		= 0; 
	this->m_jobSub		= 0;
	this->m_lvlSub		= 0;
	this->pHP			= 0;
	this->pMP			= 0;
	this->pConvHP		= 0;
	this->pConvMP		= 0;
	this->pHPP			= 0;
	this->pMPP			= 0;
	this->m_isCasting   = false;
	this->m_isRangedAttacking = false;
	this->m_isUsingItem = 0;

	this->r.fire = 0;
	this->r.ice = 0;
	this->r.wind = 0;
	this->r.earth = 0;
	this->r.thunder = 0;
	this->r.water = 0;
	this->r.light = 0;
	this->r.dark = 0;

	this->r.slash = 0;
	this->r.pierce = 0;
	this->r.handtohand = 0;
	this->r.impact = 0;
	this->r.range = 0;

	this->m_speed		= 0x28;
	this->m_speedAlt	= 0x28;

	this->m_modDef 		= 0;

	this->m_expCurrent	= 0;
	this->m_pTarget     = NULL;
	this->inBattle		= FALSE;
	this->combatAction  = 0;
	this->seeNodes		= FALSE;
	this->onTransport   = 0;
	this->targNode		= 0;
	this->nodeAction	= 0;
	this->m_isHealing	= false;
	this->m_healingStartTime = 0;
	this->m_modHPP		= 0;
	this->m_modMPP		= 0;

	this->inventory = new CItemContainer(LOC_INVENTORY, this);
	this->mogsafe = new CItemContainer(LOC_MOGSAFE, this);
	this->storage = new CItemContainer(LOC_STORAGE, this);
	this->moglocker = new CItemContainer(LOC_MOGLOCKER, this);

	this->m_tradeID		= 0;

	memset(&loc,			0,	sizeof(struct location));
	memset(&this->k,		0,	sizeof(struct keyitems));
	memset(&this->sp,		0,	sizeof(struct spells));
	memset(&this->stats,	0,	sizeof(struct stats));
	memset(&this->equip,	0,	sizeof(equip));
	for(unsigned char i = 0; i < 32; i++)
		m_status[i] = 0x00FF;

	memset(&this->m_questLogs, 0, sizeof(this->m_questLogs));
	memset(&this->m_missionLogs, 0, sizeof(this->m_missionLogs));
	memset(&this->m_assaultLog, 0, sizeof(this->m_assaultLog));
	memset(&this->m_campaignLog, 0, sizeof(this->m_campaignLog));
	memset(&this->m_crystallineLog, 0, sizeof(this->m_crystallineLog));
	for(unsigned char i = 0; i <= 5; i++)
		m_missionLogs[i].current = 0xFFFFFFFF;

	this->m_copCurrent	= 0;
	this->m_acpCurrent	= 0;

	this->m_currentTitle = 0;

	this->h.nameFlag	= 0;
	this->boundaryId	= 0;
	this->invitePending = 0;
	this->inParty		= 0;
	this->setParty(NULL);

	this->m_lostExp		= 0;
	this->m_hasRaise	= 0;
	this->m_hasTractor	= 0;

	this->eventQ.reset();

	memset(&this->m_shop,	0,  sizeof(m_shop));
	this->m_shopNation = 9;
	this->m_shopInvSlot = 0;
	this->m_shopQuantity = 0;

	this->m_synthContainer = NULL;
	this->m_synthRecipe = 0;
	this->m_synthResult = 0;

	this->m_expChain.chainNum = 0;
	this->m_expChain.chainStamp = 0;

	this->m_pPet = NULL;
	
	this->m_pTreasurePool = NULL;

	this->m_costume = 0;
	
	unlockAttributes();
	unlockCommList();
	unlockRecastSpellList();
	unlockRecastAbilityList();

}

CChar::~CChar(void)
{
	//DO NOT ENABLE THIS, MAY DEADLOCK ! (SHOULD BE FIXED, BUT I DON'T RECOMMEND IT)
	//this->setUdpPort(0);
	//this->setTargId(0);

	lockAttributes();
	delete attributeSemaphore;

	lockCommList();
	delete commListSemaphore;

	lockRecastSpellList();
	delete recastSpellListSemaphore;

	lockRecastAbilityList();
	delete recastAbilityListSemaphore;

	delete gil;

	delete inventory;
	delete mogsafe;
	delete storage;
	delete moglocker;
	delete hasEnmityList;
	CConsole::outDebOnly("Chars--: %d",--charnum);
}

//lock operations
bool CChar::tryLockAttributes(void)
{
	return attributeSemaphore->tryLock();
}

bool CChar::lockAttributes(void)
{
	return attributeSemaphore->lock();
}

bool CChar::unlockAttributes(void)
{
	return attributeSemaphore->unlock();
}

bool CChar::tryLockRecastSpellList(void)
{
	return recastSpellListSemaphore->tryLock();
}

bool CChar::lockRecastSpellList(void)
{
	return recastSpellListSemaphore->lock();
}

bool CChar::unlockRecastSpellList(void)
{
	return recastSpellListSemaphore->unlock();
}

bool CChar::tryLockRecastAbilityList(void)
{
	return recastAbilityListSemaphore->tryLock();
}

bool CChar::lockRecastAbilityList(void)
{
	return recastAbilityListSemaphore->lock();
}

bool CChar::unlockRecastAbilityList(void)
{
	return recastAbilityListSemaphore->unlock();
}


bool CChar::lockCommList(void)
{
	return commListSemaphore->lock();
}

bool CChar::unlockCommList(void)
{
	return commListSemaphore->unlock();
}

// add a command to all chars in list with the matching condition
void CChar::insertComm(int cond, CComm comm, bool hasLock) 
{
	CChar * currChar;
	T2List<unsigned int,CChar*>* lsMembers = NULL;
	//CConsole::outDebOnly("CChar::insertComm()\n");
	if (!hasLock) {
		CGlobal::waitCharList(); //Wait for the char list to become free.
	}
	switch (cond) {
		case CHAR_INALLIANCE:
			//Verify the player has a party
			if ( (this->getParty() != NULL) && (this->inParty) ) {
				if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
					//The party is part of an alliance, now check all of the parties in the alliance for members
					this->getParty()->getAlliance()->lockAllianceList();
					for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
						//Get the first party from the alliance
						CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
						//Verify the pointer of the party is not null.
						if (subParty != NULL) {
							subParty->lockPartyList();
							//Check every member inside of the alliance party
							for (unsigned int y = 0; y < subParty->members.size(); y++) {
								CChar * partyChar = subParty->members.at(y);
								if ( (partyChar != NULL) && (partyChar->getId() != this->getId()) ) {
									partyChar->addCommList(comm);
								}
							}
							subParty->unlockPartyList();
						}
					}
					this->getParty()->getAlliance()->unlockAllianceList();

					//Not in an alliance
				} else {
					//Check every member inside of the party
					this->getParty()->lockPartyList();
					for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
						CChar * partyChar = this->getParty()->members.at(y);
						if ( (partyChar != NULL) && (partyChar->getId() != this->getId()) ) {
							partyChar->addCommList(comm);
						}
					}
					this->getParty()->unlockPartyList();
				}
			}
			break;

		case CHAR_INLS:
			if (this->getLinkshell()==NULL)
				break;

			lsMembers = this->getLinkshell()->getOnlineMemberList();
			lsMembers->lock();

			//CConsole::outDebOnly("Members in LS: %d",lsMembers->getSize());
			for(unsigned int k = 0; k < lsMembers->getSize(); k++) {
				currChar = lsMembers->at(k);
				if (currChar!=this)
				{
					//CConsole::outDebOnly("Sending message to char at pos %d: %s",k,currChar->getName());
					currChar->addCommList(comm);
				}
			}

			lsMembers->unlock();
			lsMembers = NULL;
			break;

		case CHAR_INRANGE: 
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != this->getId()) && (currChar->getId() != 0)){
						if(CUtil::distance(currChar->getPos(), this->getPos()) < 50) {
							currChar->addCommList(comm);
						}
					}
				}
			}
			break;
		
		case CHAR_INRANGE_SELF: 
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != 0) ){
						if(CUtil::distance(currChar->getPos(), this->getPos()) < 50) {
							currChar->addCommList(comm);
						}
					}
				}
			}
			break;

		case CHAR_INRANGE_SHOUT:
		case CHAR_INZONE:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != this->getId()) && (currChar->getId() != 0) ){
						currChar->addCommList(comm);
					}
				}
			}
			break;

		case CHAR_INZONE_SELF:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != 0)){
						currChar->addCommList(comm);
					}
				}
			}
			break;

		case CHAR_ALL:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if (currChar->getId() != 0){
						currChar->addCommList(comm);
					}
				}
			}
			break;

		default:
			break;

	}
	if (!hasLock)
		CGlobal::exitCharList();
}

// add a packet to all chars in list with the matching condition
void CChar::insertPacket(int cond, CBasicPacket packet) {
	CChar * currChar;
	CGlobal::waitCharList(); //Wait for the char list to become free.
	switch (cond) {
		
		case CHAR_INRANGE: 
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != this->getId()) && (currChar->getId() != 0)){
						if(CUtil::distance(currChar->getPos(), this->getPos()) < 50) {
							currChar->quePacket(packet);
						}
					}
				}
			}
			break;
		
		case CHAR_INRANGE_SELF: 
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != 0) ){
						if(CUtil::distance(currChar->getPos(), this->getPos()) < 50) {
							currChar->quePacket(packet);
						}
					}
				}
			}
			break;
			
		case CHAR_INZONE:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != this->getId()) && (currChar->getId() != 0) ){
						currChar->quePacket(packet);
					}
				}
			}
			break;

		case CHAR_INZONE_SELF:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == this->getZone()) && (currChar->getId() != 0)){
						currChar->quePacket(packet);
					}
				}
			}
			break;

		case CHAR_ALL:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if (currChar->getId() != 0) {
						currChar->quePacket(packet);
					}
				}
			}
			break;
		
		case CHAR_INPARTY:
			//Verify the player has a party
			if ((this->getParty() != NULL) && (this->inParty)) {
				this->getParty()->lockPartyList();
				for(unsigned int k = 0; k < this->getParty()->members.size(); k++) {
					//Get the next person in the party
					currChar = this->getParty()->members.at(k);
					//Verify the party member isn't the caller of the function and verify their ID isn't 0
					if( (currChar->getId() != this->getId()) && (currChar->getId() != 0) ){
						currChar->quePacket(packet);
					}
				}
				this->getParty()->unlockPartyList();

			}
			break;
		case CHAR_INPARTY_SELF:
			//Verify the player has a party
			if ((this->getParty() != NULL) && (this->inParty)) {
				this->getParty()->lockPartyList();
				for(unsigned int k = 0; k < this->getParty()->members.size(); k++) {
					//Get the next person in the party
					currChar = this->getParty()->members.at(k);
					//Verify their ID isn't 0
					if ( currChar->getId() != 0 ) {
						currChar->quePacket(packet);
					}
				}
				this->getParty()->unlockPartyList();
			}
			break;

		case CHAR_INALLIANCE:
			//Verify the player has a party
			if ( (this->getParty() != NULL) && (this->inParty) ) {
				if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
					this->getParty()->getAlliance()->lockAllianceList();
					//The party is part of an alliance, now check all of the parties in the alliance for members
					for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
						//Get the first party from the alliance
						CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
						//Verify the pointer of the party is not null.
						if (subParty != NULL) {
							subParty->lockPartyList();
							//Check every member inside of the alliance party
							for (unsigned int y = 0; y < subParty->members.size(); y++) {
								CChar * partyChar = subParty->members.at(y);
								if ( (partyChar != NULL) && (partyChar->getId() != this->getId()) ) {
									partyChar->quePacket(packet);
								}
							}
							subParty->unlockPartyList();
						}
					}
					this->getParty()->getAlliance()->unlockAllianceList();
					//Not in an alliance
				} else {
					this->getParty()->lockPartyList();
					//Check every member inside of the party
					for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
						CChar * partyChar = this->getParty()->members.at(y);
						if ( (partyChar != NULL) && (partyChar->getId() != this->getId()) ) {
							partyChar->quePacket(packet);
						}
					}
					this->getParty()->unlockPartyList();
				}
			}
			break;
		case CHAR_INALLIANCE_SELF:
			//Verify the player has a party
			if ( (this->getParty() != NULL) && (this->inParty) ) {
				if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
					this->getParty()->getAlliance()->lockAllianceList();
					//The party is part of an alliance, now check all of the parties in the alliance for members
					for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
						//Get the first party from the alliance
						CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
						//Verify the pointer of the party is not null.
						if (subParty != NULL) {
							subParty->lockPartyList();
							//Check every member inside of the alliance party
							for (unsigned int y = 0; y < subParty->members.size(); y++) {
								CChar * partyChar = subParty->members.at(y);
								if (partyChar != NULL) {
									partyChar->quePacket(packet);									
								}
							}
							subParty->unlockPartyList();
						}
					}
					this->getParty()->getAlliance()->unlockAllianceList();
					//Not in an alliance
				} else {
					//Check every member inside of the party
					this->getParty()->lockPartyList();
					for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
						CChar * partyChar = this->getParty()->members.at(y);
						if (partyChar != NULL) {
							partyChar->quePacket(packet);
						}
					}
					this->getParty()->unlockPartyList();
				}
			}
			break;
		default:
			break;

	}
	CGlobal::exitCharList();
}

// add a packet to all chars in list with the matching condition
void CChar::insertPacketGlob(int cond, CBasicPacket packet, CBaseEntity *source) 
{
	CChar * currChar;
	//CConsole::outDebOnly("CChar::insertPacketGlob");
	CGlobal::waitCharList(); //Wait for the char list to become free.
	switch (cond) {
		
		case CHAR_INRANGE: 
		case CHAR_INRANGE_SELF:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == source->getZone())&& (currChar->getId() != 0)){
						if(CUtil::distance(currChar->getPos(), source->getPos()) < 50) {
							currChar->quePacket(packet);
						}
					}
				}
			}
			break;
			
		case CHAR_INZONE:
		case CHAR_INZONE_SELF:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if((currChar->getZone() == source->getZone()) && (currChar->getId() != 0)){
						currChar->quePacket(packet);
					}
				}
			}
			break;

		case CHAR_ALL:
			for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
				currChar = CGlobal::getCCharAt(k);
				if (currChar != NULL) {
					if (currChar->getId() != 0){
						currChar->quePacket(packet);
					}
				}
			}
			break;

		default:
			break;

	}
	CGlobal::exitCharList();
}

int i = 0;
// update a chars position and store it to the DB.
void CChar::updatePos(union PacketData pd){

	unsigned short currTarg;

	if(this->zoning)
		return;

	memcpy(&(this->loc.p.x), (char *)(pd.i+1), sizeof(int));
	memcpy(&(this->loc.p.y), (char *)(pd.i+2), sizeof(int));
	memcpy(&(this->loc.p.z), (char *)(pd.i+3), sizeof(int));

	memcpy(&(this->movCount), (char *)pd.c+0x12, 2);
	
	memcpy(&(this->loc.p.rotation), (char *)(pd.c+20), sizeof(char));

	memcpy(&currTarg, (char *)pd.c+22, sizeof(unsigned short));
	
	this->m_pTarget = CZoneHandler::getInstance()->getMobByTarg(currTarg, this->getDisplayZone());
	if(!this->m_pTarget) {
		this->m_pTarget = CZoneHandler::getInstance()->getNpcByTarg(currTarg, this->getDisplayZone());
		if(!this->m_pTarget) {
			this->m_pTarget = CChar::getCChar(0, "", currTarg);
			if(!this->m_pTarget) {
				this->m_pTarget = NULL;
			}
		}
	}

	if(currTarg >= 0x0700) {
		//Targetting a spawnpoint
		this->targNode = 0x0800 - currTarg;
		//CConsole::outDeb("Targetting Node: %i\n",this->targNode); //DEBUG
	} else {
		this->targNode = 0;
	}
	
	if (i % 3 == 0) {
		if((CLuaManager::regionList[this->getZone()].size() > 0)) {
			for(unsigned int a = 0; a < CLuaManager::regionList[this->getZone()].size(); a++) {
				if (CUtil::withinDistance(CLuaManager::regionList[this->getZone()].at(a).x1,CLuaManager::regionList[this->getZone()].at(a).x2,CLuaManager::regionList[this->getZone()].at(a).y1,CLuaManager::regionList[this->getZone()].at(a).y2,CLuaManager::regionList[this->getZone()].at(a).z1,CLuaManager::regionList[this->getZone()].at(a).z2,this->getPos())) {
					CLuaManager::OnRegionEnter(this, CLuaManager::regionList[this->getZone()].at(a).ID);
				}
			}
		}
	}
	else if(i % 32 == 0 ) { //approximately 9~10 seconds
		i=0;
		this->writeChar();
	}
	i++;

}

// send a tell
int CChar::sendTell(unsigned char * buf, union PacketData pd, unsigned short num){

 	char name[16];
	memcpy(name,pd.c+5,15);

	if(pd.uc[20] == '@') {
		return this->atCommand(buf, pd.c+20, num);
	}
	else 
	{

		CComm comm;
		memcpy(comm.fromc, this->m_name, 16);
		memset(pd.c+5+strlen(pd.c+5), 0, 15-strlen(pd.c+5));

		comm.pData = pd;
		comm.ident = 0x03;
		comm.lenght= pd.uc[1];

		CChar *t = NULL; //new CChar();
		t = CChar::getCChar(0, name, 0);

		CConsole::outTime("tell: %s: %s",name,pd.uc+20);
		memcpy(comm.message,pd.uc+20,strlen((char*)(pd.uc+20)));
		if(t) {
			comm.setComType(0xb6);
			t->addCommList(comm);
		}
		else {
			CConsole::outErr("Tell reciever not found");
			return this->sendErr(buf, 0x7d, num);
		}
	}

	return 0;
}


// send std chat and intercept commands
int CChar::sendSay(unsigned char* data, union PacketData pd, unsigned short num){
	CComm comm;
	comm.pData = pd;
	if(pd.uc[6] == '@') {
		return this->atCommand(data, pd.c+6, num);
	}
	else 
	{
		int range = -1;
		switch(pd.uc[4]){
			case 8: //EMOTE
			case 0: //SAY
				range = CHAR_INRANGE;
				break;
			case 4:	//PARTY
				if (this->getParty()==NULL)
					return 0;
				range = CHAR_INALLIANCE;
				break;
			case 5: //LINKSHELL
				range = CHAR_INLS;
				break;
			case 1: //SHOUT
				range = CHAR_INRANGE_SHOUT;
				break;
			default:
				range = CHAR_INZONE;
				break;
		}
		comm.setComType(0xb5);
		comm.lenght = pd.uc[1];
		memset(comm.message , 0, 255);
		memcpy(comm.message , pd.c+6, strlen(pd.c+6));
		memcpy(&comm.ident, pd.uc+4, 1);
		memcpy(comm.fromc, this->m_name, 16);
		this->insertComm(range, comm, false);
	}
	return 0;
}

// sets the chars position
void CChar::setPosition(float x, float y, float z) {
	this->loc.p.x = x;
	this->loc.p.y = y;
	this->loc.p.z = z;
	this->quePacket(CPositionPacket((CBaseEntity*)this,this->loc.p));
}

// perform forced zoning - uses zone lines
int CChar::zoneChar(unsigned char to){
	char query[8192];
	unsigned long count;
	MYSQL_RES * res;
	MYSQL_ROW row;

	sprintf(query,"SELECT tox, toy, toz, rotation FROM zoneline WHERE tozone = 0x%x ",to);
	count = (int) strlen(query);
	strcpy(query+count, " LIMIT 1");
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	if(!res)
	{
		CConsole::outErr("Error looking up zone");
	} else if(!mysql_num_rows(res)){
		mysql_free_result(res);
		CConsole::outErr("Error looking up zone");	
	} else {
		row = mysql_fetch_row(res);
		memcpy(&(this->loc.p.x), row[0], sizeof(float));
		CConsole::outTime("%d %d %d", row[0][0],row[1][0],row[2][0]);
		memcpy(&(this->loc.p.y), row[1], sizeof(float));
		memcpy(&(this->loc.p.z), row[2], sizeof(float));
		memcpy(&(this->loc.p.rotation), row[3], sizeof(unsigned short));
		
		mysql_free_result(res);
	}
	this->zoneChar(to,this->getPos());
	return 0;
}

// perform forced zoning - without zone lines
int CChar::zoneChar(unsigned char to,position pos){
	char query[8192];
	unsigned long count;
	this->zoning = true;

	//CComm comm1;
	//comm1.setComType(0x101);
	//comm1.ptr = (LPVOID *)this;
	//this->insertComm(CHAR_INZONE, comm1, false);
	this->insertPacket(CHAR_INZONE,CCharPacket(this,false));

	this->seeNodes = false;  //turn off nodes whenever char zones
	this->inEvent = false;
	// Despawn any pets
	CreateThread(0,0,despawnPet,this,0,0);

	Sleep(10);

	//Check if we are actually changing zones or just re-zoning in the same one
	if(this->getZone() != to) {
		this->loc.prevzone = (unsigned char)this->getZone();
		this->setZone(to);

		//update zone counter since we are changing zones
		CZoneHandler::getInstance()->decreaseZoneCounter(this->loc.prevzone);
		CZoneHandler::getInstance()->increaseZoneCounter(this->getZone());
	}

	
	memcpy(&(this->loc.p),&pos,sizeof(position));

	sprintf(query, "UPDATE chars SET udate=CURRENT_TIMESTAMP, boundary = %d, pos_x = %f, pos_y = %f, pos_z = %f, pos_rot = %i, pos_zone = %i, pos_prevzone = %i WHERE charid = %i",
		this->boundaryId,this->loc.p.x,this->loc.p.y,this->loc.p.z,this->loc.p.rotation,this->getZone(),this->loc.prevzone,this->getId());
	count = (int) strlen(query);
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	CComm *comm = new CComm();
	comm->setComType(0x5E0C);
	comm->flags = 0x01000000;

	this->addCommList(*comm);
	CConsole::outTime("Zoning %s\n", this->m_name);
	return 0;
}

// request to zone
int CChar::chip(unsigned char *pd, unsigned short num){

	unsigned short port = 54230;
	memset(pd, 0, 28);
	pd[0] = 0x0B;
	pd[1] = 0x0E;
	memcpy(pd+2, &num, sizeof(num));

	pd[4] = 2;
	memcpy(pd+8, &(this->clientConn.servIP), sizeof(this->clientConn.servIP));
	memcpy(pd+0x0C, &port, sizeof(unsigned short));//pd->us[6] = 54230;

	return 28;
}

// prepare char
int CChar::changeZone(){
	lockAttributes();
	char query[8192];
	unsigned long count, i;
	unsigned char hash[16];

	this->seeNodes = false;  //turn off nodes whenever char zones
	this->inEvent = false;
	// Despawn any pets
	CreateThread(0,0,despawnPet,this,0,0);

	this->enc.key[4] += 2;

	md5((unsigned char*)(this->enc.key), hash, 20);

	for(i = 0; i < 16; i++)
		if(hash[i] == 0)
			break;
	memset(hash+i, 0, 16-i);
	memcpy(this->enc.hash, hash, 16);

	CBlowfish::init((char *)this->enc.hash, 16, this->enc.P, this->enc.S[0]);

	unlockAttributes();

	//key has been updated, change the zoning process
	this->zoning = 2;
	return 0;
}

// actually zone
void CChar::zoner(void)
{
	short eventid = -1;

	this->newSpawnIdList->lock();
	while(this->newSpawnIdList->getSize()>0)
	{
		this->newSpawnIdList->removeAt(0);
	}
	
	// Remove enmity from the previous zone
	this->clearHasEnmityList();

	// reset tp
	this->h.tp = 0;

	this->newSpawnIdList->unlock();
	//reset the spellrecastlist
	this->recastSpellList.clear();

	//Check to see if this is an airship zone-in
	if(this->onTransport > 0)
		eventid = CTransportHandler::getTransportEvent(this);  	//Run the transport cutscene
	
	if(eventid == -1)  //If eventid not set from getTransportEvent, use onZoneIn eventid
		eventid = CLuaManager::OnZoneIn(this, this->loc.prevzone);

	if(eventid >= 0)
		this->inEvent = true;
	if( (this->getAnimation() == 0x01) || (this->getAnimation() == 0x2C) ) {
		this->setAnimation(0);
	}

	this->quePacket(CDownloadingDataPacket());
	this->quePacket(CDownloadingAppearancePacket(this));
	this->quePacket(CZoneInPacket(this, eventid));
	this->quePacket(CDownloadingUnknownPacket());

	
	//If we have a treasure pool, leave it and join a new one for this zone
	if (this->hasTreasurePool()) {
		this->getTreasurePool()->removeMember(this);
	}
	//Finds the treasure pool for this zone
	this->getTreasurePool();

}

void CChar::clearHasEnmityList() {
	this->hasEnmityList->lock();
	CBaseEntity * tmpEntity = NULL;
	while (this->hasEnmityList->getSize()>0)
	{
		//Remove the player from the mobs enmity list
		tmpEntity = this->hasEnmityList->at(0);
		if (tmpEntity != NULL) {
			//If its not null 
			//Verify that it is a mob
			if ( tmpEntity->isMob() ) {
				((CNpc *)tmpEntity)->removeEnmity(this);
			}
		}
		this->hasEnmityList->removeAt(0);
		tmpEntity = NULL;
	}
	this->hasEnmityList->unlock();
}

// zone using a zoneline
int CChar::zoneLine(union PacketData pd){
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[8192];
	int count;
	this->zoning = 1;

	//CComm comm;
	//comm.setComType(0x101);
	//comm.ptr = (LPVOID *)this;
	//this->insertComm(CHAR_INZONE, comm, false);
	this->insertPacket(CHAR_INZONE,CCharPacket(this,false));

	strcpy(query, "SELECT tozone, tox, toy, toz, rotation FROM zoneline WHERE fromline = '");
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	count += mysql_real_escape_string(mysqlConn, query+count, pd.c+4, 4);
	strcpy(query+count, "' LIMIT 1");
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn



	if(!res)
	{
		CConsole::outErr("Error looking up zone");
		goto frloop;
	}
	if(!mysql_num_rows(res)){
		mysql_free_result(res);
		//leaving moghouse
		if( (( this->getZone() > 0xd5) & (this->getZone() < 0xdb)) || (this->getZone() > 0xfc) ) 
		{
			//CConsole::outDebOnly("This may be handled wrong (zoning) - Message Dantalion if something goes wrong");
			unsigned char zone=(unsigned char)this->getZone();
			this->setZone(this->loc.prevzone);
			this->loc.prevzone=zone;
			CZoneHandler::getInstance()->decreaseZoneCounter(this->loc.prevzone);
			CZoneHandler::getInstance()->increaseZoneCounter(this->getZone());
			memset(&(this->loc.p.x), 0, sizeof(float)*3);
			//we could use script handling 
		} 
		else 
		{
			CConsole::outErr("Zone line not found, using default");
frloop:		this->setZone(0xf5);
			CZoneHandler::getInstance()->decreaseZoneCounter(this->loc.prevzone);

			this->loc.prevzone = (unsigned char)this->getZone();

			CZoneHandler::getInstance()->increaseZoneCounter(this->getZone());

			memset(&(this->loc.p.x), 0, sizeof(float)*3);
		}
	} 
	else 
	{
		row = mysql_fetch_row(res);
		this->loc.prevzone = (unsigned char)this->getZone();
		this->setZone((unsigned short)row[0][0]);
		// zone to resident or non-resident moghouse
		CConsole::outDebOnly("Nation: %i, ZoningTo: %2x", this->st.n.home, this->getZone());
		if(this->getZone() > 0xFC) {
			switch((unsigned int)this->st.n.home)
			{
				case 2: // windy citizen
					if(this->getZone() == 0xFD) // sandy resident area
						this->setZone(0xD8);  // sandy non-resident area
					if(this->getZone() == 0xFE) // bastok resident area
						this->setZone(0xD9);  // bastok non-resident area
					break;
				case 1: // bastok citizen
					if(this->getZone() == 0xFD) // sandy resident area
						this->setZone(0xD8);  // sandy non-resident area
					if(this->getZone() == 0xFF) // windy resident area
						this->setZone(0xDA);  // windy non-resident area
					break;
				case 0: // sandy citizen
					if(this->getZone() == 0xFE) // bastok resident area
						this->setZone(0xD9);  // bastok non-resident area
					if(this->getZone() == 0xFF) // windy resident area
						this->setZone(0xDA);  // windy non-resident area
					break;
			}
		}

		//update zone counters
		CZoneHandler::getInstance()->decreaseZoneCounter(this->loc.prevzone);
		CZoneHandler::getInstance()->increaseZoneCounter(this->getZone());

		memcpy(&(this->loc.p.x), row[1], sizeof(float));
		memcpy(&(this->loc.p.y), row[2], sizeof(float));
		memcpy(&(this->loc.p.z), row[3], sizeof(float));
		memcpy(&(this->loc.p.rotation), row[4], 2);
		mysql_free_result(res);
	}

	sprintf(query, "UPDATE chars SET udate=CURRENT_TIMESTAMP, pos_x = %f, pos_y = %f, pos_z = %f, pos_rot = %i, pos_zone = %i, pos_prevzone = %i WHERE charid = %i",
		this->getXPos(),this->getYPos(),this->getZPos(),this->getRot(),this->getZone(),this->loc.prevzone,this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	CConsole::outTime("Zoning %s\n", this->getName());

	return 0;
}

// process @commands
int CChar::atCommand(unsigned char * buf, char * data, unsigned short num){
	int dummy=0, dummy1=0, dummy2=0;
	if(!_strnicmp(data, "@misstest",9))
	{
		CBaseEntity* targ;
        if (this->m_pTarget==NULL)
        {
            targ=this;
        }
        else
        {
			targ=this->m_pTarget;
        }
		this->missedAttackAction(targ);
	}
	else if(!_strnicmp(data, "@rotdiff", 7))
	{
		CBaseEntity* targ;
		if (this->m_pTarget==NULL)
        {
            targ=this;
        }
        else
        {
			targ=this->m_pTarget;
        }
		this->isFaceing(targ->loc.p);
	}
	else if(!_strnicmp(data, "@mobrotdiff", 10))
	{
		CBaseEntity* targ;
		if (this->m_pTarget==NULL)
        {
            targ=this;
        }
        else
        {
			targ=this->m_pTarget;
        }
		targ->isFaceing(this->loc.p);
	}
	else if(!memcmp(data, "@globalLP", 9))
	{
		CLinkshell::addLinkPearl(this,CLinkshell::getLinkshellById(1));
	}
	else if(!memcmp(data, "@zone ", 6)){
		sscanf(data, "@zone %hx", (&dummy));
		if( dummy > 0xff) {
			dummy = 0xff;
		}	
		this->zoning = 1;
		this->onTransport = 0;
		this->zoneChar(dummy);
	}
	else if(!memcmp(data, "@zonepos ", 8)){
		int dummyzone = 0x00;
		sscanf(data, "@zonepos %hx %d %d %d", (&dummyzone), &dummy, &dummy1, &dummy2);
		if( dummyzone > 0xff) {
			dummyzone = 0xff;
		}	
		this->zoning = 1;
		this->onTransport = 0;
		position pos;
		pos.x = (float)dummy;
		pos.y = (float)dummy1;
		pos.z = (float)dummy2;
		this->zoneChar(dummyzone, pos);
	}

	
	else if(!memcmp(data, "@spawn ", 6)){
		char dummyname[30];
		memset(dummyname, 0, 30);

		sscanf(data, "@spawn %s", &dummyname);

		CMobHandler::getInstance()->tmpSpawn(0, dummyname, this);

	}
	else if(!memcmp(data, "@goto ", 6)){
		char dummyname[16];
		memset(dummyname, 0, 16);

		sscanf(data, "@goto %s", &dummyname);
 
		CChar * dummychar = CChar::getCChar(0, dummyname, 0);
 
		if( (dummychar != NULL) && (dummychar != this) ) {
			position pos;
			pos.x = dummychar->getXPos();
			pos.y = dummychar->getYPos();
			pos.z = dummychar->getZPos();
			if (this->getZone() != dummychar->getZone()) {
				this->zoning = 1;
				this->onTransport = 0;
				this->zoneChar((unsigned char)dummychar->getZone(), pos);
			}
			else {
				this->setPosition(pos.x, pos.y, pos.z);
			}
		}
		else {
			char msg[255];
			memset(msg,0,255);
			sprintf(msg,"@goto: %s is not online.",dummyname);
			CPacket::sendServerTell("Axium",  this->getName(), msg);
			CConsole::outTime("@goto by %s failed: %s is not online.", this->getName(), dummyname);
		}
	}
	else if(!memcmp(data, "@hp ", 4)){
		sscanf(data, "@hp %i", &(dummy));
		if(this->isDead()) {
			return 0;
		}
		if((unsigned int)dummy > (unsigned)this->getMaxHp()) {
			dummy = this->getMaxHp();
		}
		this->h.hp = dummy;
		this->h.hpp = (unsigned char)ceil(((float)dummy / (float)this->getMaxHp()) * 100);

		if( this->getHp() == 0) {
			this->die();
		}
		this->updateHealth();
	}
	else if(!memcmp(data, "@mp ", 4)){
		sscanf(data, "@mp %i", &(dummy));
		if((unsigned int)dummy > (unsigned int)this->getMaxMp()) {
			dummy = this->getMaxMp();
		}
		this->h.mp = dummy;
		this->h.mpp = (unsigned char)((float)dummy / (float)this->getMaxMp()) * 100;
		this->updateHealth();
	}
	else if(!memcmp(data, "@tp ", 4)){
		sscanf(data, "@tp %i", &(dummy));
		if((unsigned int)dummy > 300) {
			dummy = 300;
		}
		this->h.tp = (unsigned int)dummy;
		this->updateHealth();
	}
	else if(!memcmp(data, "@speed ", 7)){
		sscanf(data, "@speed %hx", &(dummy));
		this->m_speed = dummy;
		this->updateChar();
		
	}
	else if(!memcmp(data, "@raise", 6)){
	
		//this->m_hasRaise = 3;
		this->sendRaiseMenu();
		
	}
	else if(!memcmp(data, "@resetRC", 7)){
	
		this->lockRecastAbilityList();
		this->recastAbilityList.clear();
		this->unlockRecastAbilityList();
		this->quePacket(CCharSkillPacket(this));
		
	}
	else if(!memcmp(data, "@tractor", 8)){
	
		this->m_hasTractor = true;
		this->m_tractorPos.x = 0;
		this->m_tractorPos.y = 0;
		this->m_tractorPos.z = 0;
		this->sendTractorMenu();
		
	}
	else if(!memcmp(data, "@showm ", 7)){
		sscanf(data, "@showm %i %i %i", &dummy, &dummy1, &dummy2);
		
		if(this->m_pTarget != NULL) {
			this->insertPacket(CHAR_INRANGE_SELF,CMessagePacketTwo(this, this->m_pTarget, 0, 0,  dummy));
		} else {
			this->insertPacket(CHAR_INRANGE_SELF,CMessagePacketTwo(this, this, dummy1, dummy2, dummy));
		}
		
	}
	else if(!memcmp(data, "@settitle ", 10)){
		sscanf(data, "@settitle %d", &dummy);
		setTitle(dummy);
	} else if (!memcmp(data, "@setfame ", 9)) {
		sscanf(data, "@setfame %d %d", &dummy, &dummy1);
		if (dummy < 0 || dummy > 3) { 
			dummy = 0;
		}
		if (dummy1 < 0) {
			dummy1 = 0;
		}
		this->setFame((unsigned char)dummy,(unsigned short)dummy1);
	}
	else if(!memcmp(data, "@mobws ", 7)){
		sscanf(data, "@mobws %d", &dummy);
		if(this->m_pTarget==NULL) {
			this->h.tp = 100;
			this->weaponskill(dummy, 10, this);
		} else {
			if(this->m_pTarget->getObjectType() == OBJ_NPC) 
				if( ((CNpc*)(this->m_pTarget))->type == 0x07)
					return 0;
			this->m_pTarget->h.tp = 100;
			this->m_pTarget->weaponskill(dummy, 10, this);
		}
	}
	else if(!memcmp(data, "@sp ", 4))
    {
        sscanf(data, "@sp %d %d", (&dummy), &dummy1);
       
        unsigned int targ;
        if (this->m_pTarget==NULL)
        {
            targ=this->getId();
        }
        else
        {
			targ=this->m_pTarget->getId();
        }

		CActionPacket* actionPacket=new CActionPacket();
		actionPacket->setActorId(this->getId());
		actionPacket->setCategorie(4);//Magic - End
		actionPacket->setStartAnimationCategorie(0x00);//Magic
		actionPacket->setStartAnimation(0x0000);
		actionPacket->setParameter1(dummy);
		actionPacket->setUnknown(0x50000000);
		apActionEntry newEntry;
		memset(&newEntry,0,sizeof(newEntry));
		newEntry.unknownValue=0x00;
		newEntry.targetAnimationID=dummy;
		newEntry.unknownValue2=0x000;
		newEntry.parameter2=0x000A;
		newEntry.messageType=dummy1;
		newEntry.additionalMessages=0x00000000;
		newEntry.dynamisTimerFlag=0x0;
		actionPacket->setTargetID(1,targ);
		actionPacket->setActionEntry(1,1,&newEntry);
		
		actionPacket->finalizePacket();
		//actionPacket->savePacket();

        this->insertPacket(CHAR_INRANGE_SELF, *actionPacket);
    }
	else if(!memcmp(data, "@spCat ", 7))
    {
		int dummy3 = 0;
        sscanf(data, "@spCat %d %d %d %d", (&dummy), &dummy1, &dummy2, &dummy3);
       
        unsigned int targ;
        if (this->m_pTarget==NULL)
        {
            targ=this->getId();
        }
        else
        {
			targ=this->m_pTarget->getId();
        }

		CActionPacket* actionPacket=new CActionPacket();
		actionPacket->setActorId(this->getId());
		actionPacket->setCategorie(dummy);//Magic - End
		actionPacket->setStartAnimationCategorie(0x00);//Magic
		actionPacket->setStartAnimation(0x0000);
		//actionPacket->setParameter1(dummy2+256);
		actionPacket->setUnknown(0x50000000);
		apActionEntry newEntry;
		memset(&newEntry,0,sizeof(newEntry));
		newEntry.unknownValue=0x00;
		newEntry.targetAnimationID=dummy2;
		newEntry.unknownValue2=dummy3;
		newEntry.parameter2=0xa;
		newEntry.messageType=dummy1;
		newEntry.additionalMessages=0x00000000;
		newEntry.dynamisTimerFlag=0x0;
		actionPacket->setTargetID(1,targ);
		actionPacket->setActionEntry(1,1,&newEntry);
		
		actionPacket->finalizePacket();
		//actionPacket->savePacket();

        this->insertPacket(CHAR_INRANGE_SELF, *actionPacket);
    }
	else if(!memcmp(data, "@costume ", 9))
	{
		sscanf(data, "@costume %x", &(dummy));
		this->m_costume = dummy;
		this->updateChar();
	}
	else if (!memcmp(data, "@rdmgear ", 9)){
		this->addItem(15249, 0, 1); // Duelist's Chapeau +1
		this->addItem(16108, 0, 1); // Goliard Chapeau
		this->addItem(15229, 0, 1); // Warlock's Chapeau +1

		this->addItem(14562, 0, 1); // Morrigan's Robe
		this->addItem(13788, 0, 1); // Dalmatica +1
		this->addItem(14504, 0, 1); // Duelist's Tabard +1
		this->addItem(14570, 0, 1); // Goliard Saio
		this->addItem(14477, 0, 1); // Warlock's Tabard +1
		this->addItem(14381, 0, 1); // Mahatma Houppelande
		this->addItem(14368, 0, 1); // Blood Scale Mail
		this->addItem(14489, 0, 1); // Nashira Manteel
		this->addItem(11315, 0, 1); // Royal Redingote
	
		this->addItem(14977, 0, 1); // Morrigan's Cuffs
		this->addItem(14825, 0, 1); // Dusk Gloves +1
		this->addItem(14913, 0, 1); // Duelist's Gloves +1
		this->addItem(14059, 0, 1); // Blood Finger Gauntlets
		this->addItem(14017, 0, 1); // Master Caster Bracelets
		
		this->addItem(15641, 0, 1); // Morrigan's Slops
		this->addItem(14281, 0, 1); // Blood Cuisses
		this->addItem(15565, 0, 1); // Warlock's Tights +1
		this->addItem(15577, 0, 1); // Nashira Seraweels
    
		this->addItem(15727, 0, 1); // Morrigan's Pigaches
		this->addItem(15735, 0, 1); // Goliard Clogs

		this->addItem(15545, 0, 1); // Tamas Ring
		this->addItem(15800, 0, 1); // Omega Ring
		this->addItem(15807, 0, 1); // Balrahn's ring
    
		this->addItem(14808, 0, 1); // Novio Earring
		this->addItem(14809, 0, 1); // Novia Earring
		this->addItem(15990, 0, 1); // Delta Earring
		this->addItem(14724, 0, 1); // Moldavite Earring
		this->addItem(14812, 0, 1); // Locquacious Earring
		this->addItem(15962, 0, 1); // Static Earring
		this->addItem(15963, 0, 1); // Magnetic Earring

		this->addItem(13652, 0, 1); // Umbra Cape
		this->addItem(15471, 0, 1); // Merciful Cape
		this->addItem(15472, 0, 1); // Altruistic Cape
		this->addItem(13657, 0, 1); // Mahatma Cape

		this->addItem(17652, 0, 1); // Joyeuse   
		this->addItem(18276, 0, 1); // Excalibur
		this->addItem(12296, 0, 1); // Genbu's Shield

		this->addItem(19023, 0, 1); // Staff Strap
		this->addItem(17546, 0, 1); // Vulcan's Staff
		this->addItem(17548, 0, 1); // Aquilo's Staff
		this->addItem(17550, 0, 1); // Auster's Staff
		this->addItem(17552, 0, 1); // Terra's Staff
		this->addItem(17554, 0, 1); // Jupiter's Staff
		this->addItem(17556, 0, 1); // Neptune's Staff
		this->addItem(17560, 0, 1); // Pluto's Staff
		this->addItem(17558, 0, 1); // Apollo's Staff
    
		this->addItem(17277, 0, 1); // Hedgehog Bomb
		this->addItem(18245, 0, 1); // Aureole
		     
		this->addItem(15918, 0, 1); // Witch Sash
		this->addItem(15458, 0, 1); // Ninurta's Sash
		this->addItem(15928, 0, 1); // Lycopodium Sash
		    
		this->addItem(15540, 0, 1); // Orochi Nodowa+1
		this->addItem(13153, 0, 1); // dark torque
		this->addItem(13154, 0, 1); // enhancing torque
		this->addItem(13155, 0, 1); // enfeebling torque
		this->addItem(13156, 0, 1); // elemental torque
          
		this->sendFinishInvStor();
	}
	else if (!memcmp(data, "@thfgear ", 9)){
		this->addItem(15240, 0, 1); // homam zucchetto
		this->addItem(15270, 0, 1); // Walahra turban
		this->addItem(13915, 0, 1); // optical hat
		this->addItem(16107, 0, 1); // denali bonnet
		this->addItem(13928, 0, 1); // hecatomb cap +1
      
		this->addItem(14569, 0, 1); // Denali Jacket
		this->addItem(14550, 0, 1); // Skadi's cuirie
		this->addItem(11287, 0, 1); // Antares Harness
     
		this->addItem(14825, 0, 1); // Dusk Gloves +1
		this->addItem(14077, 0, 1); // hecatomb mittens +1
		this->addItem(14984, 0, 1); // denali wristbands
    
	    this->addItem(15576, 0, 1); // homam cosciales
		this->addItem(14181, 0, 1); // hecatomb leggings +1
         
		this->addItem(15661, 0, 1); // homam gambieras
		this->addItem(14309, 0, 1); // hecatomb subligar +1

		this->addItem(15543, 0, 1); // rajas ring
		this->addItem(15548, 0, 1); // Mars's ring
    
		this->addItem(13415, 0, 1); // pixie earring
		this->addItem(14713, 0, 1); // triumph earring +1

		this->addItem(16241, 0, 1); // Cuchulain's mantle
    
	    this->addItem(18270, 0, 1); // Mandau
		this->addItem(18015, 0, 1); // blau dolch
		this->addItem(18020, 0, 1); // mercurial kris
		this->addItem(17440, 0, 1); // kraken club

		this->addItem(18707, 0, 1); // fire bomblet
   
		this->addItem(15514, 0, 1); // love torque
           
		this->sendFinishInvStor();
	}
    else if(!memcmp(data, "@pmcp ", 6))
    {
		sscanf(data, "@pmcp %d", &(dummy));
       
        unsigned int targ;
        if (this->m_pTarget==NULL)
        {
            targ=this->getId();
        }
        else
        {
            targ=this->m_pTarget->getId();
        }
		CActionPacket* actionPacket=new CActionPacket();
		actionPacket->loadPremade(this->getId(),targ,dummy);
		actionPacket->finalizePacket();
        this->quePacket(*actionPacket);
    }
	else if(!memcmp(data, "@inject ", 8))
		return CPacket::loadPacket(data+8, buf, num);

	else if(!memcmp(data, "@easteregg", 10)){
		this->addItem(0x40EE, 0, 1);
		this->addItem(12332, 0, 1);
		this->addItem(12523, 0, 1);
		this->addItem(12551, 0, 1);
		this->addItem(12679, 0, 1);
		this->addItem(12807, 0, 1);
	    this->addItem(12935, 0, 1);
		this->addItem(13074, 0, 1);
		this->addItem(13215, 0, 1);
		this->addItem(13358, 0, 1);
		this->addItem(13358, 0, 1);
		this->addItem(13505, 0, 1);
		this->addItem(13505, 0, 1);
		this->addItem(13606, 0, 1);
		this->addItem(17012, 0, 1);
		this->addItem(17174, 0, 1);
		this->addItem(17326, 0, 1);
		this->addItem(17644, 0, 1);
		this->sendFinishInvStor();
	} 
	else if(!memcmp(data, "@additem ", 9)){
		sscanf(data, "@additem %d %d", &dummy, &dummy1);
		if(dummy1 < 1)
			dummy1 = 1;
		if(dummy > 20000) {
			 CPacket::sendServerTell("Axium",  this->getName(), "ItemID out of range!");
			return 0;
		}
		this->addItem(dummy, 0, dummy1);
		this->sendFinishInvStor();
	}
	else if(!memcmp(data, "@addtreasure ", 13)){
		sscanf(data, "@addtreasure %d %d", &dummy, &dummy1);
		if (dummy1 < 1)
			dummy1 = 1;
		this->getTreasurePool()->addItem(dummy,dummy1,this);
	}
	else if(!memcmp(data,"@addkeyitem ", 12)) {
		sscanf(data, "@addkeyitem %x", &dummy);
		if (!this->hasKeyItem(dummy)) {
			this->k.keyList[dummy/8] ^= (int)pow((double)2.0,(int)(dummy % 8));
			this->updateKeyItems();
		}
	}
	else if(!memcmp(data,"@allkeyitems", 12)) {
		memset(&this->k.keyList, 0xFF, sizeof(this->k.keyList));
		this->updateKeyItems();
	}
	else if(!memcmp(data,"@takekeyitem ", 13)) {
		sscanf(data, "@takekeyitem %x", &dummy);
		if (this->hasKeyItem(dummy)) {
			this->k.keyList[dummy/8] ^= (int)pow((double)2.0,(int)(dummy % 8));
			this->k.seenList[dummy/8] ^= (int)pow((double)2.0,(int)(dummy % 8));
			this->updateKeyItems();
		}
	}
	else if(!memcmp(data,"@addspell ", 10)) {
		sscanf(data, "@addspell %d", &dummy);
		if (!this->hasSpell(dummy)) {
			this->sp.spellList[dummy/8] ^= (int)pow((double)2.0,(int)(dummy % 8));
			this->updateSpells();
		}
	}
	else if(!memcmp(data,"@allspells", 10)) {
		memset(&this->sp.spellList, 0xFF, sizeof(this->sp.spellList));
		this->updateSpells();
	}
	else if(!memcmp(data,"@takespell ", 11)) {
		sscanf(data, "@takespell %d", &dummy);
		if (this->hasSpell(dummy)) {
			this->sp.spellList[dummy/8] ^= (int)pow((double)2.0,(int)(dummy % 8));
			this->updateSpells();
		}
	}
	else if(!memcmp(data,"@addws ", 7)) {
		sscanf(data, "@addws %d", &dummy);
		this->learnWeaponSkill(dummy);
		this->updateAbilities();
	}
	else if(!memcmp(data,"@addstatus ", 11)) {
		sscanf(data, "@addstatus %d", &dummy);
		this->addStatusIcon(dummy);
	}
	else if(!memcmp(data,"@takestatus ", 12)) {
		sscanf(data, "@takestatus %d", &dummy);
		this->removeStatusIcon(dummy);
	}
	else if(!memcmp(data,"@msg1 ",6)) {
		sscanf(data, "@msg1 %d %d %d", &dummy, &dummy1, &dummy2);
		CBaseEntity * pEntity;
		pEntity = this->m_pTarget;
		if(!pEntity) {
			this->quePacket(CMessagePacket(this, this, dummy1, dummy2, dummy));
			return 0;
		} else {
			this->quePacket(CMessagePacket(this, pEntity, dummy1, dummy2, dummy));
			return 0;
		}
	}
	else if(!memcmp(data,"@msg2 ",6)) {
		sscanf(data, "@msg2 %d %d %d", &dummy, &dummy1, &dummy2);
		CBaseEntity * pEntity;
		pEntity = this->m_pTarget;
		if(!pEntity) {
			this->quePacket(CMessagePacketTwo(this, this, dummy1, dummy2, dummy));
			return 0;
		} else {
			this->quePacket(CMessagePacketTwo(this, pEntity, dummy1, dummy2, dummy));
			return 0;
		}
	}
	else if(!memcmp(data,"@spmsg ",7)) {
		int messageID = 0;
		int dummy3 = 0;
		int dummybool = 0;
		sscanf(data, "@spmsg %d %d %d %d %d %d", &messageID, &dummy, &dummy1, &dummy2, &dummy3, &dummybool);
		sendSpecialMessage(messageID, dummy, dummy1, dummy2, dummy3, ((dummybool == 1)?true:false));
		return 0;
	}
	else if(!memcmp(data, "@ship ", 6)){
		sscanf(data, "@ship %x", &dummy);
		return CPacket::loadPacket2("sanship.dat", buf, num,dummy);
	}
	else if(!memcmp(data, "@cs ", 4)){
		sscanf(data, "@cs %x", &dummy);
		CEventPacket * ptrCep = new CEventPacket(dummy);
		ptrCep->createPacket(this);
	}
	else if(!memcmp(data, "@release", 8)){
		sscanf(data, "@release %x", &dummy);
		CConsole::outDebOnly("Dummy: %i", dummy);
		this->release(dummy);
	}
	else if(!memcmp(data,"@animation %x", 11)){
		sscanf(data, "@animation %x", &dummy);
		this->setAnimation((unsigned char)dummy);
        this->updateChar();
	} 
	else if(!memcmp(data,"@newMobs", 8)){
		for(unsigned short z = 0; z < 256; z++)
			CUtil::fillNewTable(z);
	} 
	else if(!memcmp(data,"@zoneR %x", 7)){
		sscanf(data, "@zoneR %x", &dummy);
		CZoneHandler::getInstance()->loadZoneList(dummy);
	} 
	else if(!memcmp(data,"@setlook %x", 9)){
		sscanf(data, "@setlook %x", &dummy);

		if(this->m_pTarget == NULL)
			return 0;

		if(!this->m_pTarget->isMob())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}

		mob->setLookVal(dummy);
		mob->sendUpdate();
	
	} else if(!memcmp(data,"@setunknown %x", 12)){
		sscanf(data, "@setunknown %x", &dummy);

		if(this->m_pTarget == NULL)
			return 0;

		if(!this->m_pTarget->isMob())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}

		mob->unknown=dummy;
		mob->sendUpdate();
	
	}  else if(!memcmp(data,"@setflag %x", 9)){
		sscanf(data, "@setflag %x", &dummy);

		if(this->m_pTarget == NULL)
			return 0;

		if(!this->m_pTarget->isMob())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}

		mob->flag=dummy;
		mob->sendUpdate();
	
	} else if(!memcmp(data,"@mobani %x", 8)){
		sscanf(data, "@mobani %x", &dummy);

		if(this->m_pTarget == NULL)
			return 0;

		if(this->m_pTarget->isPlayer())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}
		
		CConsole::outTime("NAME %s ID: %X",mob->m_name, mob->m_id);
		CConsole::outTime("ID: %d",mob->getId());
		mob->ownerId = this->getId();
		mob->m_pTarget = (CBaseEntity *)this;
		mob->incombat = true;
		mob->update = true;
		mob->setAnimation((unsigned char)dummy);
		mob->victim = this;
		if(mob->getObjectType() == OBJ_MOB)
			mob->updateEnmity(this, 0,1);

		return 0;

	} else if(!memcmp(data,"@charm", 6)){

		if(this->m_pTarget == NULL)
			return 0;

		if(this->m_pTarget->isPlayer())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}
		
		CConsole::outTime("NAME %s ID: %X",mob->m_name, mob->m_id);
		CConsole::outTime("ID: %d",mob->getId());
		//mob->ownerId = this->getId();
		//mob->m_pTarget = (CBaseEntity *)this;
		//mob->victim = this;
		if(mob->getObjectType() == OBJ_MOB)
			mob->updateEnmity(this, 0,1);

		//mob->removeEnmity(this);
		mob->updateEnmity(this,0,1);
		mob->isPet(true);
		//mob->incombat = true;
		mob->moveState = 2;
		mob->type = 0x07;
		mob->targ_p = mob->getPos();
		mob->unknown = 0x99;
		m_pPet = mob;
		mob->sendUpdate();
		sendCharUpdate();

		return 0;

	} else if(!memcmp(data,"@leave", 6)){

		if(this->m_pTarget == NULL)
			return 0;

		if(this->m_pTarget->isPlayer())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}
		
		CConsole::outTime("NAME %s ID: %X",mob->m_name, mob->m_id);
		CConsole::outTime("ID: %d",mob->getId());
		//mob->ownerId = this->getId();
		//mob->m_pTarget = (CBaseEntity *)this;
		//mob->victim = this;
		if(mob->getObjectType() == OBJ_MOB)
			mob->updateEnmity(this, 0,1);

		mob->removeEnmity(this);
		mob->isPet(false);
		//mob->incombat = true;
		mob->moveState = 0;
		mob->type = 0x07;
		mob->unknown = 0x0;
		m_pPet = NULL;
		mob->sendUpdate();
		sendCharUpdate();

		return 0;

	}else if(!memcmp(data,"@mobatk %d", 8)){
		sscanf(data, "@mobatk %d", &dummy);

		if(this->m_pTarget == NULL)
			return 0;

		if(this->m_pTarget->isPlayer())
			return 0;

		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;
		
		if(!mob) {
			return 0;
		}
		CNpc * mob2;
		mob2 = (CNpc*)CZoneHandler::getInstance()->getMob(dummy, this->getDisplayZone());
		if (mob2 != NULL) {
			CConsole::outTime("NAME %s ID: %X",mob->m_name, mob->m_id);

			mob->ownerId = mob2->getId();
			mob->m_pTarget = (CBaseEntity *)mob2;
			mob->incombat = true;
			mob->update = true;
			mob->setAnimation((unsigned char)dummy);
			//mob->victim = mob2;
			if(mob->getObjectType() == OBJ_MOB)
				mob->updateEnmity(mob2, 0,1);
		}
		return 0;
	}
	else if(!memcmp(data,"@altani %x", 8)){
		sscanf(data, "@altani %x", &dummy);
		CNpc * mob;
		mob = (CNpc*)this->m_pTarget;

		if(!mob) {
			return 0;
		}
		
		CConsole::outTime("NAME %s ID: %X",mob->m_name, mob->m_id);
		mob->setAltAnimation((unsigned char)dummy);
		mob->sendUpdate();
		return 0;
	}

	else if(!memcmp(data,"@mobstat %x", 9)){
		sscanf(data, "@mobstat %x", &dummy);

		CNpc * npc;
		npc = (CNpc*)this->m_pTarget;

		if(!npc) {
			return 0;
		}

		CConsole::outTime("NAME %s ID: %X",npc->getName(),npc->getId());
		CConsole::outTime("stat  %X",npc->status);


		for(unsigned int j = 0; j < CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].size(); j++) {
			if(CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].at(j)->getId() == npc->getId()) {
				memcpy(&CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].at(j)->status, &dummy, 1);
				break;
			}
		}
		int size = 0;
		buf[size] = 0x0E;
		buf[size+1] = (unsigned char)((0x33+strlen((const char*)npc->m_name)+sizeof(int))/4)*2;
 		memcpy(buf+size+0x30, &(npc->look), 4);
		memcpy(buf+size+0x34, npc->m_name, strlen((const char*)npc->m_name));
		
		CConsole::outTime("NAME %s ID: %X",npc->m_name,npc->m_id);
 		memcpy(buf+size+2, &num, sizeof(short));
		 	memcpy(buf+size+4, &npc->m_id, 4);

			unsigned short targId = npc->getTargId();
	 		memcpy(buf+size+8, &targId, 2);
			buf[size+0x1f] = npc->getAnimation();
			buf[size+10] = npc->type;
 			buf[size+11] = (unsigned char)npc->getRot();
			buf[size+0x1c] = 0x28;		
			buf[size+0x1d] = 0x28;
 			buf[size+0x1E] = 0x64;
 			buf[size+0x27] = 0x20;
 			
			memcpy(buf+size+12, &npc->loc.p.x, 4);
 			memcpy(buf+size+16, &npc->loc.p.y, 4);
			memcpy(buf+size+20, &npc->loc.p.z, 4);
 			memcpy(buf+size+24, &npc->flag, 4);
			
			memcpy(buf+size+0x2c, &npc->ownerId, 4);
			memcpy(buf+size+0x20, &dummy, 4);
 			size += buf[size+1]*2;
		//dummy;
         return size;
	}
    else if(!memcmp(data, "@npc", 4)){
		float smallest = -1.0;
		float dist = 0.0;
		CNpc * npc;
		if (CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].size() > 0){
			for(unsigned int i = 0; i < (CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].size()); i++){
				npc = CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].at(i);
				dist = CUtil::distance(this->getPos(), npc->getPos());
				if (dist < smallest || smallest == -1.0){
					dummy = i;
					smallest = dist;
				}
			}
			npc = CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].at(dummy);
			CConsole::outTime("The closest npc is: %x %x",npc->getId(),npc->getTargId());
		}
		else {
			CConsole::outTime("No NPCs on this zone!");
		}
	}

    else if(!memcmp(data, "@gm", 3)){
		this->h.nameFlag ^= FLAG_GM;
		this->updateChar();
	} 
	else if(!memcmp(data, "@status ", 8)){
		sscanf(data, "@status %d ", &dummy);
		this->h.flags = dummy;
		this->updateChar();
	} 
	else if(!memcmp(data, "@where",6)){
		char dummyname[16];
		memset(dummyname, 0, 16);

		sscanf(data, "@where %s", &dummyname);

		char msg[255];
		memset(msg,0,255);
		CChar * dummychar = CChar::getCChar(0, dummyname, 0);

		if( (dummychar) && (dummychar->m_name[0] != 0x00) ) {
			memset(msg,0,255);
			sprintf(msg,"Name: %s, ZoneId: %x",dummychar->m_name,dummychar->getZone());
		    CPacket::sendServerTell("Axium",  this->getName(), msg);
			Sleep(10);
			memset(msg,0,255);
			sprintf(msg,"X:%.3f, Y:%.3f, Z:%.3f, Rotation:%i", dummychar->getXPos(),dummychar->getYPos(),dummychar->getZPos(),dummychar->getRot()); 
		    CPacket::sendServerTell("Axium",  this->getName(), msg);
			CConsole::outTime("%s  X:%f  Y:%f  Z:%f  ZoneID: %x", dummychar->getName(), dummychar->getXPos(), dummychar->getYPos(), dummychar->getZPos(), dummychar->getZone());
		} else {
			memset(msg,0,255);
			sprintf(msg,"Name: %s, ZoneId: %x",this->getName(),this->getZone());
		    CPacket::sendServerTell("Axium",  this->getName(), msg);
			Sleep(10);
			memset(msg,0,255);
			sprintf(msg,"X:%.3f, Y:%.3f, Z:%.3f, Rotation:%i", this->getXPos(),this->getYPos(),this->getZPos(),this->getRot()); 
		    CPacket::sendServerTell("Axium",  this->getName(), msg);
			CConsole::outTime("%s  X:%f  Y:%f  Z:%f  ZoneID: %x", this->getName(), this->getXPos(), this->getYPos(), this->getZPos(), this->getZone());
		}
	}
	else if(!memcmp(data, "@shownodes", 10)){
		//if this->h.flags has FLAG_GM show spawnpoint nodes
		if(this->h.nameFlag & FLAG_GM){
			if(this->seeNodes == TRUE){
				CPacket::sendServerTell("Axium", this->getName(), "SHOWNODES: Off");
				this->seeNodes = FALSE;
			} else {
				CPacket::sendServerTell("Axium", this->getName(), "SHOWNODES: You are not a GM!");
			}
			return 0;
		}

		if( (this->m_pTarget == NULL) || (this->m_pTarget->getId() == this->getId()) ) {
			if(this->seeNodes == TRUE){
				CPacket::sendServerTell("Axium", this->getName(), "SHOWNODES: Off");
				this->seeNodes = FALSE;
			} else {
				CPacket::sendServerTell("Axium", this->getName(), "SHOWNODES: Invalid target. Please target a MOB/NPC.");
			}
			return 0;
		}

		if(this->seeNodes == FALSE){
			CPacket::sendServerTell("Axium", this->getName(), "SHOWNODES: On");
			this->seeNodes = TRUE;
			this->nodeAction = 10;
		//	CreateThread(0,0,SpawnPointServ,this,0,0);
		} else {
			CPacket::sendServerTell("Axium", this->getName(), "SHOWNODES: Off");
			this->seeNodes = FALSE;
		}
	}
	else if(!memcmp(data, "@addnode", 8)){
		//if spawnpoint nodes are visible and target is a node, add node to spawnpoint
		char msg[255];
		memset(msg,0,255);
		if(this->targNode > 0) {
			sprintf(msg, "ADDNODE: Adding new node after %i.", this->targNode);
			CPacket::sendServerTell("Axium", this->getName(), msg);
			this->nodeAction = 1;
		} else {
			CPacket::sendServerTell("Axium", this->getName(), "ADDNODE: Failed. Please target a node.");
		}
	}
	else if(!memcmp(data, "@delnode", 8)){
		//if spawnpoint nodes are visible and target is a node, and node is not last node, delete node from spawnpoint
		char msg[255];
		memset(msg,0,255);
		if(this->targNode > 1) {
			sprintf(msg,"DELNODE: Deleting node %i.", this->targNode);
			CPacket::sendServerTell("Axium", this->getName(), msg);
			this->nodeAction = 2;
		} else if(this->targNode = 1) {
			CPacket::sendServerTell("Axium", this->getName(), "DELNODE: Cannot delete root node.");
		} else {
			CPacket::sendServerTell("Axium", this->getName(), "DELNODE: Failed. Please target a node.");
		}
	}
	else if(!memcmp(data, "@delallnodes", 12)){
		if(this->h.nameFlag & FLAG_GM) {
			CNpc * target;
			target = (CNpc*)this->m_pTarget;
			
			if(this->seeNodes) {
				if(!target || (this->getId()==target->getId())) {
					CPacket::sendServerTell("Axium", this->getName(), "DELALLNODES: Failed. Please target a MOB/NPC.");
					return 0;
				} else {
					CPacket::sendServerTell("Axium", this->getName(), "DELALLNODES: Deleting all nodes.");
					this->nodeAction = 3;
				}
			} else {
				CPacket::sendServerTell("Axium", this->getName(), "DELALLNODES: Failed. ShowNodes not On.");
				return 0;
			}
		} else
			CPacket::sendServerTell("Axium", this->getName(), "DELALLNODES: You are not a GM!");
	}
	else if(!memcmp(data, "@pos ", 5)){
		sscanf(data, "@pos %d %d %d", &dummy, &dummy1, &dummy2);
		this->setPosition((float)dummy, (float)dummy1, (float)dummy2);
	} 
	else if(!memcmp(data, "@posr ", 6)){
		sscanf(data, "@posr %d %d %d", &dummy, &dummy1, &dummy2);
		this->setPosition((this->getXPos()+(float)dummy),  (this->getYPos()+(float)dummy1),  (this->getZPos()+(float)dummy2));
	} 
	else if(!memcmp(data, "@togglejob ", 11)){
		sscanf(data, "@togglejob %d", &dummy);
		if( (dummy > 20) || (dummy == 0) )
			return 0;
		this->j.unlocked ^= (0x00000001 << dummy);
		this->writeChar();
		this->quePacket(CCharJobInfoPacket(this));
	}
	else if(!memcmp(data, "@changejob ", 11)){
		sscanf(data, "@changejob %d %d", &dummy, &dummy1);
		if(dummy < 1 || dummy > 0x14) {
			CPacket::sendServerTell("Axium", this->getName(), "CHANGEJOB: Jobid out of range!");
		} else {
			this->cmjob((unsigned char)dummy, (unsigned char)dummy1);
		}
	}
	else if(!memcmp(data, "@changesjob ", 12)){
		sscanf(data, "@changesjob %d %d", &dummy, &dummy1);
		if(dummy < 1 || dummy > 0x14) {
			CPacket::sendServerTell("Axium", this->getName(), "CHANGESJOB: Jobid out of range!");
		} else {
			this->csjob((unsigned char)dummy, (unsigned char)dummy1);
		}
		
	}
	else if(!memcmp(data, "@setlvlcap ", 11)){
		sscanf(data, "@setlvlcap %d", &dummy);
		this->setLevelCap(dummy);
	}
	else if(!memcmp(data, "@setgil ", 8)){
		sscanf(data, "@setgil %d", &dummy);
		this->setGil(dummy);
	}
	else if(!memcmp(data, "@weather ", 9)){
		sscanf(data, "@weather %d", &dummy);
		struct weather updateWeather;
		updateWeather.changeTime = 0;
		updateWeather.weatherEffect = dummy;
		this->insertPacket(CHAR_INZONE_SELF,CWeatherPacket(updateWeather));
	}
	else if(!memcmp(data, "@skill ", 7)){
		sscanf(data, "@skill %d %d", &dummy, &dummy1);
		this->setSkill(dummy, dummy1);
		if( (dummy >= 48) && (dummy <= 56) ) {
			unsigned char rank = 0;
			rank = ((dummy1-1)/100);
			this->s.rank[dummy] = rank;
		}
		this->writeChar();
		this->quePacket(CCharSkillPacket(this));
	}
	else if(!memcmp(data, "@setquest ", 10)){
		sscanf(data, "@setquest %d %d %d", &dummy, &dummy1, &dummy2);
		this->setQuestMissionLogStatus(dummy, dummy1, dummy2);
	}
	else if(!memcmp(data, "@setmission ", 12)){
		sscanf(data, "@setmission %d %d %d", &dummy, &dummy1, &dummy2);
		this->setQuestMissionLogStatus(dummy+10, dummy1, dummy2);
	}
	else if(!memcmp(data, "@ds", 3)){
		CreateThread(0,0,despawnPet,this,0,0);
	}
	else if(!memcmp(data, "@debug", 6))
		this->debugMsg("charid %x targid %x", this->getId(), this->getTargId());
	return 0;
}

int CChar::logout(unsigned char * data, unsigned short num, bool removeFromList)
{
	memset(data, 0, 28);
	data[0] = 0xB;
	data[1] = 0xE;
	memcpy(data+2, &num, sizeof(num));
	data[4] = 1;
	//CComm comm;
	//comm.setComType(0x101);
	//comm.ptr = (LPVOID *)this;
	//this->insertComm(CHAR_INZONE, comm, false);
	this->insertPacket(CHAR_INZONE,CCharPacket(this,false));

	//save the character on proper logout
	this->writeChar();
	//this->remove(false);
	this->clientConn.polSocket = 0;

	if (removeFromList)
	{
		//remove Member from linkshell
		if (this->getLinkshell()!=NULL)
			this->getLinkshell()->removeOnlineMember(this);

		//remove Member from party
		this->forcePartyLeave();

		//remove Char from online list
		
		CGlobal::removeCChar(this);
	}

	if (this->m_logoutType!=0x03)
	{
		//reinsert the connection info into the lobby con list
		CGlobal::lockLobbyConList();
		struct connections*	newCon = new struct connections(this->clientConn);
		newCon->time = (unsigned int)time(NULL);
		CGlobal::lobbyConList.push_back(*newCon);
		CGlobal::unlockLobbyConList();
	}
	return 28;
}

int CChar::allowLogout(unsigned char * data, unsigned short num){

	memset(data, 0, 0x50);
	data[0] = 0x4b;
	data[1] = 0x0a;
	memcpy(data+2, &num, sizeof(num));
	data[0x14] = 0x4c;
	data[0x15] = 0x1e;
	
	CComm *comm = new CComm();
	comm->setComType(0x0b);
	this->addCommList(*comm);


	return 80;
}

void CChar::updateKeyItems() 
{	
	this->quePacket(CUpdateKeyItemsPacket(this,0));
	this->quePacket(CUpdateKeyItemsPacket(this,1));
	this->quePacket(CUpdateKeyItemsPacket(this,2));
	this->quePacket(CUpdateKeyItemsPacket(this,3));
}

void CChar::updateSpells() 
{  
	this->quePacket(CUpdateSpellsPacket(this));
}

void CChar::updateAbilities() 
{  
	this->quePacket(CUpdateAbilitiesPacket(this));
}



void CChar::release(){
	this->quePacket(CReleasePacket(this));
	this->inEvent = false;
}

//Custom release for scripters
void CChar::release(unsigned char releaseType){
	this->quePacket(CReleasePacket(this,releaseType));
	this->inEvent = false;
}

bool CChar::equipWeapon(unsigned char invSlotID, unsigned char equipSlotID) {
	CWeaponItem * item;
	item = (CWeaponItem*)getItem(LOC_INVENTORY, invSlotID);
	if(item == NULL) {
		CConsole::outDebOnly("No item in inventory slot %i.", invSlotID);
		return false;
	}
	unsigned int modelID = item->getModelId();
	if(equipSlotID == item->getEquipSlotId()) {
		switch(equipSlotID) {
			case SLOT_MAIN:
				this->l.main = item->getModelId();
				if(item->getSkillType() == SKILL_H2H) {
					if(item->getSubModelId() > 0)
						this->l.sub = item->getSubModelId();
					else
						this->l.sub = item->getModelId();
				}
				break;
			case SLOT_RANGED:
				this->l.range = item->getModelId();
				break;
		}
	} else if( (item->getEquipSlotId() == SLOT_MAIN) && (equipSlotID == SLOT_OFF) ) {
		// For now, equip only if SLOT_MAIN is a single handed weapon
		CWeaponItem * weapon = NULL;
		weapon = getWeaponItem(SLOT_MAIN);
		if(weapon != NULL) {
			switch(weapon->getSkillType()) {
				case WEAPON_DAG:
				case WEAPON_SWD:
				case WEAPON_AXE:
				case WEAPON_KAT:
				case WEAPON_CLB:
					switch(item->getSkillType()) {
						case WEAPON_DAG:
						case WEAPON_SWD:
						case WEAPON_AXE:
						case WEAPON_KAT:
						case WEAPON_CLB: {
							if(item->getSubModelId() > 0)
								this->l.sub = item->getSubModelId();
							else
								this->l.sub = item->getModelId();
							equip[equipSlotID] = invSlotID;
							this->updateAbilities();  
							return true;
						}
						default:
							CConsole::outDebOnly("Item %i is not equipable in equip slot %i", item->getId(), equipSlotID);
							return false;
					}
				default:
					CConsole::outDebOnly("Item cannot be equipped with current main weapon.");
					return false;
			}
		}
	} else {
		CConsole::outDebOnly("Item %i is not equipable in equip slot %i", item->getId(), equipSlotID);
		return false;
	}

	this->h.tp = 0;
	equip[equipSlotID] = invSlotID;
	this->updateAbilities();  
	return true;
}

bool CChar::equipArmor(unsigned char invSlotID, unsigned char equipSlotID) {
	CArmorItem * item;
	item = (CArmorItem*)getItem(LOC_INVENTORY, invSlotID);
	if(item == NULL) {
		CConsole::outDebOnly("No item in inventory slot %i.", invSlotID);
		return false;
	}
	unsigned int modelID = item->getModelId();
	if(equipSlotID == item->getEquipSlotId()) {
		if(equip[SLOT_EAR2] == invSlotID)
			return false;
		if(equip[SLOT_RING2] == invSlotID)
			return false;

		unsigned char removeSlotID = 0;
		removeSlotID = item->getRemoveSlotId();
		if(removeSlotID > 0) {
			unequipItem(removeSlotID);
			this->quePacket(CEquipPacket(0, removeSlotID));
		}

		switch(equipSlotID) {
			case SLOT_HEAD: {
				CArmorItem * armor;
				armor = getArmorItem(SLOT_BODY);
				if(armor != NULL) {
					unsigned char removeSlotID;
					removeSlotID = armor->getRemoveSlotId();
					if(removeSlotID == SLOT_HEAD) {
						removeStatMod(SLOT_BODY);
						unequipItem(SLOT_BODY);
						quePacket(CEquipPacket(0, SLOT_BODY));
					}
				}
				this->l.head = item->getModelId();
				break;
			}
			case SLOT_BODY:
				this->l.body = item->getModelId();
				if(item->getSubModelId() > 0) {
					this->l.hands = item->getSubModelId();
				}
				break;
			case SLOT_HANDS: {
				CArmorItem * armor;
				armor = getArmorItem(SLOT_BODY);
				if(armor != NULL) {
					unsigned char removeSlotID;
					removeSlotID = armor->getRemoveSlotId();
					if(removeSlotID == SLOT_HANDS) {
						removeStatMod(SLOT_BODY);
						unequipItem(SLOT_BODY);
						quePacket(CEquipPacket(0, SLOT_BODY));
					}
				}
				this->l.hands = item->getModelId();
				break;
			}
			case SLOT_LEGS:
				this->l.legs = item->getModelId();
				if(item->getSubModelId() > 0) {
					this->l.feet = item->getSubModelId();
				}
				break;
			case SLOT_FEET: {
				CArmorItem * armor;
				armor = getArmorItem(SLOT_LEGS);
				if(armor != NULL) {
					unsigned char removeSlotID;
					removeSlotID = armor->getRemoveSlotId();
					if(removeSlotID == SLOT_FEET) {
						removeStatMod(SLOT_LEGS);
						unequipItem(SLOT_LEGS);
						quePacket(CEquipPacket(0, SLOT_LEGS));
					}
				}
				this->l.feet = item->getModelId();
				break;
			}
			case SLOT_OFF:
				this->l.sub = item->getModelId();
				break;
		}

		equip[equipSlotID] = invSlotID;

	} else if(equipSlotID == SLOT_EAR2) {
		if(item->getEquipSlotId() == SLOT_EAR1) {
			if(equip[SLOT_EAR1] == invSlotID)
				return false;

			equip[equipSlotID] = invSlotID;
		} else {
			CConsole::outDebOnly("Item %i is not equipable in equip slot %i", item->getId(), equipSlotID);
			return false;
		}
	} else if(equipSlotID == SLOT_RING2) {
		if(item->getEquipSlotId() == SLOT_RING1)  {
			if(equip[SLOT_RING1] == invSlotID)
				return false;

			equip[equipSlotID] = invSlotID;
		} else {
			CConsole::outDebOnly("Item %i is not equipable in equip slot %i", item->getId(), equipSlotID);
			return false;
		}
	} else {
		CConsole::outDebOnly("Item %i is not equipable in equip slot %i", item->getId(), equipSlotID);
		return false;
	}

	return true;
}

void CChar::unequipItem(unsigned char equipSlotID) {
	CArmorItem * armor = NULL;
	armor = getArmorItem(equipSlotID);
	if(armor != NULL) {
		unsigned char removeSlotID; 
		removeSlotID = armor->getRemoveSlotId();
		switch(removeSlotID) {
			case SLOT_HEAD:
				this->l.head = 0;
				break;
			case SLOT_HANDS:
				this->l.hands = 0;
				break;
			case SLOT_FEET:
				this->l.feet = 0;
				break;
		}
	}

	switch(equipSlotID) {
		case SLOT_HEAD:
			this->l.head = 0;
			break;
		case SLOT_BODY:
			this->l.body = 0;
			break;
		case SLOT_HANDS:
			this->l.hands = 0;
			break;
		case SLOT_LEGS:
			this->l.legs = 0;
			break;
		case SLOT_FEET:
			this->l.feet = 0;
			break;
		case SLOT_OFF: {
			CWeaponItem * item = NULL;
			item = getWeaponItem(SLOT_MAIN);
			if(item != NULL) {
				if(item->getSkillType() != SKILL_H2H)
					this->l.sub = 0;
			} else
				this->l.sub = 0;
			break;
		}
		case SLOT_MAIN: {
			CWeaponItem * item = NULL;
			item = getWeaponItem(equipSlotID);
			if(item == NULL)
				return;
			this->l.main = 0;
			if(item->getSkillType() == SKILL_H2H)
				this->l.sub = 0;
			break;
		}
		case SLOT_RANGED:
			this->l.range = 0;
			break;
	}

	CItem * item = NULL;
	item = getItem(LOC_INVENTORY, equip[equipSlotID]);
	if(item != NULL) {
		unsigned short itemID = item->getId();
		unsigned long quantity = item->getQuantity();
		this->quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, equip[equipSlotID], INV_NORMAL));
	}

	equip[equipSlotID] = 0;
}

void CChar::equipItem(unsigned char invSlotID, unsigned char equipSlotID){

	if(equip[equipSlotID] == invSlotID)
		return;

	if(invSlotID > 0) {
		if(equipSlotID == SLOT_RING1)
			if(equip[SLOT_RING2] == invSlotID)
				return;
		if(equipSlotID == SLOT_RING2)
			if(equip[SLOT_RING1] == invSlotID)
				return;
		if(equipSlotID == SLOT_EAR1)
			if(equip[SLOT_EAR2] == invSlotID)
				return;
		if(equipSlotID == SLOT_EAR2)
			if(equip[SLOT_EAR1] == invSlotID)
				return;
		if(equipSlotID == SLOT_MAIN)
			if(equip[SLOT_OFF] == invSlotID)
				return;
		if(equipSlotID == SLOT_OFF)
			if(equip[SLOT_MAIN] == invSlotID)
				return;
	}

	this->removeStatMod(equipSlotID);
	this->unequipItem(equipSlotID);

	if(invSlotID == 0) {
		this->quePacket(CEquipPacket(invSlotID, equipSlotID));
		this->quePacket(CDownloadingAppearancePacket(this));
		if( (equipSlotID == SLOT_MAIN) || (equipSlotID == SLOT_RANGED) )
			this->updateAbilities();
		this->updateHealth();
	} else {
		CItem * item;
		item = getItem(LOC_INVENTORY, invSlotID);
		if(item == NULL) {
			return;
		}
		unsigned char itemEquiped = false;
		if( (item->getType() != ITEM_ARMOR) && (item->getType() != ITEM_WEAPON) ) {
			CConsole::outDebOnly("Inventory item %i is not equipable into slot %i", invSlotID, equipSlotID);
			return;
		} else if(item->getType() == ITEM_ARMOR) {
			itemEquiped = equipArmor(invSlotID, equipSlotID);
		} else if(item->getType() == ITEM_WEAPON) {
			itemEquiped = equipWeapon(invSlotID, equipSlotID);
		}
		if(itemEquiped) {
			this->applyStatMod(equipSlotID);
			this->quePacket(CEquipPacket(invSlotID, equipSlotID));
			this->quePacket(CDownloadingAppearancePacket(this));
			if( (equipSlotID == SLOT_MAIN) || (equipSlotID == SLOT_RANGED) )
				this->updateAbilities();
			this->updateHealth();
			unsigned short itemID = item->getId();
			unsigned long quantity = item->getQuantity();
			this->quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, invSlotID, INV_NODROP));
		} else
			return;
	}
	writeChar();
}

void CChar::removeStatMod(unsigned char equipSlotID) {
	if(equip[equipSlotID] == 0)
		return;
	CItem * item;
	item = getItem(LOC_INVENTORY, equip[equipSlotID]);
	if(item == NULL) {
		CConsole::outDebOnly("Cannot apply stats. Item null.");
		return;
	}
	if(item->getType() == ITEM_ARMOR) {
		lockAttributes();
		this->stats.pStr -= ((CArmorItem*)item)->getModAmount(MOD_STR);
		this->stats.pDex -= ((CArmorItem*)item)->getModAmount(MOD_DEX);
		this->stats.pVit -= ((CArmorItem*)item)->getModAmount(MOD_VIT);
		this->stats.pAgi -= ((CArmorItem*)item)->getModAmount(MOD_AGI);
		this->stats.pInt -= ((CArmorItem*)item)->getModAmount(MOD_INT);
		this->stats.pMnd -= ((CArmorItem*)item)->getModAmount(MOD_MND);
		this->stats.pChr -= ((CArmorItem*)item)->getModAmount(MOD_CHR);
		this->pHP		 -= ((CArmorItem*)item)->getModAmount(MOD_HP);
		this->pMP		 -= ((CArmorItem*)item)->getModAmount(MOD_MP);
		this->m_modDef	 -= ((CArmorItem*)item)->getModAmount(MOD_DEF);
		this->m_modAcc	 -= ((CArmorItem*)item)->getModAmount(MOD_ACC);
		this->m_modAtt	 -= ((CArmorItem*)item)->getModAmount(MOD_ATT);
		this->st.atk	  = this->getAttack();

		//Conversions and % mods (to be applied at setting of Max HP and MP)
		this->pConvHP	 -=	(((CArmorItem*)item)->getModAmount(MOD_CONVMPTOHP) - ((CArmorItem*)item)->getModAmount(MOD_CONVHPTOMP));
		this->pConvMP	 -= (((CArmorItem*)item)->getModAmount(MOD_CONVHPTOMP) - ((CArmorItem*)item)->getModAmount(MOD_CONVMPTOHP));
		this->pHPP		 -= ((CArmorItem*)item)->getModAmount(MOD_HPP);
		this->pMPP		 -= ((CArmorItem*)item)->getModAmount(MOD_MPP);

		//Convert to and from HP, including % mods
		if (((signed short)(this->getMaxHp() - 1)) < this->pConvMP) {
			this->m_modMPP = (1+(((double)(this->pMPP))/100))*this->getMaxHp();
		} else {
			this->m_modMPP = (1+(((double)(this->pMPP))/100))*this->pConvMP;
		}

		//Convert to and from MP, including % mods
		if (((signed short)(this->getMaxMp() - 1)) < this->pConvHP) {
			this->m_modHPP = (1+(((double)(this->pHPP))/100))*this->getMaxMp();
		} else {
			this->m_modHPP = (1+(((double)(this->pHPP))/100))*this->pConvHP;
		}

		//Elemental resistances
		this->r.fire	 -= ((CArmorItem*)item)->getModAmount(MOD_FIRERES);
		this->r.ice		 -= ((CArmorItem*)item)->getModAmount(MOD_ICERES);
		this->r.wind	 -= ((CArmorItem*)item)->getModAmount(MOD_AIRRES);
		this->r.earth	 -= ((CArmorItem*)item)->getModAmount(MOD_EARTHRES);
		this->r.thunder	 -= ((CArmorItem*)item)->getModAmount(MOD_THUNDERRES);
		this->r.water	 -= ((CArmorItem*)item)->getModAmount(MOD_WATERRES);
		this->r.light	 -= ((CArmorItem*)item)->getModAmount(MOD_LIGHTRES);
		this->r.dark	 -= ((CArmorItem*)item)->getModAmount(MOD_DARKRES);

		unlockAttributes();
		return;
	}
	if(item->getType() == ITEM_WEAPON) {
		lockAttributes();
		this->stats.pStr -= ((CWeaponItem*)item)->getModAmount(MOD_STR);
		this->stats.pDex -= ((CWeaponItem*)item)->getModAmount(MOD_DEX);
		this->stats.pVit -= ((CWeaponItem*)item)->getModAmount(MOD_VIT);
		this->stats.pAgi -= ((CWeaponItem*)item)->getModAmount(MOD_AGI);
		this->stats.pInt -= ((CWeaponItem*)item)->getModAmount(MOD_INT);
		this->stats.pMnd -= ((CWeaponItem*)item)->getModAmount(MOD_MND);
		this->stats.pChr -= ((CWeaponItem*)item)->getModAmount(MOD_CHR);
		this->pHP		 -= ((CWeaponItem*)item)->getModAmount(MOD_HP);
		this->pMP		 -= ((CWeaponItem*)item)->getModAmount(MOD_MP);
		this->m_modDef	 -= ((CWeaponItem*)item)->getModAmount(MOD_DEF);
		this->m_modAcc	 -= ((CWeaponItem*)item)->getModAmount(MOD_ACC);
		this->m_modAtt	 -= ((CWeaponItem*)item)->getModAmount(MOD_ATT);
		this->st.atk	  = this->getAttack();

		//Conversions and % mods (to be applied at setting of Max HP and MP)
		this->pConvHP	 -=	(((CWeaponItem*)item)->getModAmount(MOD_CONVMPTOHP) - ((CWeaponItem*)item)->getModAmount(MOD_CONVHPTOMP));
		this->pConvMP	 -= (((CWeaponItem*)item)->getModAmount(MOD_CONVHPTOMP) - ((CWeaponItem*)item)->getModAmount(MOD_CONVMPTOHP));
		this->pHPP		 -= ((CWeaponItem*)item)->getModAmount(MOD_HPP);
		this->pMPP		 -= ((CWeaponItem*)item)->getModAmount(MOD_MPP);

		//Elemental resistances
		this->r.fire	 -= ((CWeaponItem*)item)->getModAmount(MOD_FIRERES);
		this->r.ice		 -= ((CWeaponItem*)item)->getModAmount(MOD_ICERES);
		this->r.wind	 -= ((CWeaponItem*)item)->getModAmount(MOD_AIRRES);
		this->r.earth	 -= ((CWeaponItem*)item)->getModAmount(MOD_EARTHRES);
		this->r.thunder	 -= ((CWeaponItem*)item)->getModAmount(MOD_THUNDERRES);
		this->r.water	 -= ((CWeaponItem*)item)->getModAmount(MOD_WATERRES);
		this->r.light	 -= ((CWeaponItem*)item)->getModAmount(MOD_LIGHTRES);
		this->r.dark	 -= ((CWeaponItem*)item)->getModAmount(MOD_DARKRES);

		unlockAttributes();
		return;
	}
	CConsole::outDebOnly("Item is not a weapon or armor.");
}

void CChar::applyStatMod(unsigned char equipSlotID) {
	if(equip[equipSlotID] == 0)
		return;
	CItem * item;
	item = getItem(LOC_INVENTORY, equip[equipSlotID]);
	if(item == NULL) {
		CConsole::outDebOnly("Cannot apply stats. Item null.");
		return;
	}
	if(item->getType() == ITEM_ARMOR) {
		lockAttributes();
		this->stats.pStr += ((CArmorItem*)item)->getModAmount(MOD_STR);
		this->stats.pDex += ((CArmorItem*)item)->getModAmount(MOD_DEX);
		this->stats.pVit += ((CArmorItem*)item)->getModAmount(MOD_VIT);
		this->stats.pAgi += ((CArmorItem*)item)->getModAmount(MOD_AGI);
		this->stats.pInt += ((CArmorItem*)item)->getModAmount(MOD_INT);
		this->stats.pMnd += ((CArmorItem*)item)->getModAmount(MOD_MND);
		this->stats.pChr += ((CArmorItem*)item)->getModAmount(MOD_CHR);
		this->pHP		 += ((CArmorItem*)item)->getModAmount(MOD_HP);
		this->pMP		 += ((CArmorItem*)item)->getModAmount(MOD_MP);
		this->m_modDef	 += ((CArmorItem*)item)->getModAmount(MOD_DEF);
		this->m_modAcc	 += ((CArmorItem*)item)->getModAmount(MOD_ACC);
		this->m_modAtt	 += ((CArmorItem*)item)->getModAmount(MOD_ATT);
		this->st.atk	  = this->getAttack();
		
		//Conversions and % mods (to be applied at setting of Max HP and MP)
		this->pConvHP	 +=	(((CArmorItem*)item)->getModAmount(MOD_CONVMPTOHP) - ((CArmorItem*)item)->getModAmount(MOD_CONVHPTOMP));
		this->pConvMP	 += (((CArmorItem*)item)->getModAmount(MOD_CONVHPTOMP) - ((CArmorItem*)item)->getModAmount(MOD_CONVMPTOHP));
		this->pHPP		 += ((CArmorItem*)item)->getModAmount(MOD_HPP);
		this->pMPP		 += ((CArmorItem*)item)->getModAmount(MOD_MPP);

		//Convert to and from HP, including % mods
		if (((signed short)(this->getMaxHp() - 1)) < this->pConvMP) {
			this->m_modMPP = (1+(((double)(this->pMPP))/100))*this->getMaxHp();
		} else {
			this->m_modMPP = (1+(((double)(this->pMPP))/100))*this->pConvMP;
		}

		//Convert to and from MP, including % mods
		if (((signed short)(this->getMaxMp() - 1)) < this->pConvHP) {
			this->m_modHPP = (1+(((double)(this->pHPP))/100))*this->getMaxMp();
		} else {
			this->m_modHPP = (1+(((double)(this->pHPP))/100))*this->pConvHP;
		}

		//Elemental resistances
		this->r.fire	 += ((CArmorItem*)item)->getModAmount(MOD_FIRERES);
		this->r.ice		 += ((CArmorItem*)item)->getModAmount(MOD_ICERES);
		this->r.wind	 += ((CArmorItem*)item)->getModAmount(MOD_AIRRES);
		this->r.earth	 += ((CArmorItem*)item)->getModAmount(MOD_EARTHRES);
		this->r.thunder	 += ((CArmorItem*)item)->getModAmount(MOD_THUNDERRES);
		this->r.water	 += ((CArmorItem*)item)->getModAmount(MOD_WATERRES);
		this->r.light	 += ((CArmorItem*)item)->getModAmount(MOD_LIGHTRES);
		this->r.dark	 += ((CArmorItem*)item)->getModAmount(MOD_DARKRES);

		unlockAttributes();
		return;
	}
	if(item->getType() == ITEM_WEAPON) {
		lockAttributes();
		this->stats.pStr += ((CWeaponItem*)item)->getModAmount(MOD_STR);
		this->stats.pDex += ((CWeaponItem*)item)->getModAmount(MOD_DEX);
		this->stats.pVit += ((CWeaponItem*)item)->getModAmount(MOD_VIT);
		this->stats.pAgi += ((CWeaponItem*)item)->getModAmount(MOD_AGI);
		this->stats.pInt += ((CWeaponItem*)item)->getModAmount(MOD_INT);
		this->stats.pMnd += ((CWeaponItem*)item)->getModAmount(MOD_MND);
		this->stats.pChr += ((CWeaponItem*)item)->getModAmount(MOD_CHR);
		this->pHP		 += ((CWeaponItem*)item)->getModAmount(MOD_HP);
		this->pMP		 += ((CWeaponItem*)item)->getModAmount(MOD_MP);
		this->m_modDef	 += ((CWeaponItem*)item)->getModAmount(MOD_DEF);
		this->m_modAcc	 += ((CWeaponItem*)item)->getModAmount(MOD_ACC);
		this->m_modAtt	 += ((CWeaponItem*)item)->getModAmount(MOD_ATT);
		this->st.atk	  = this->getAttack();

		//Conversions and % mods (to be applied at setting of Max HP and MP)
		this->pConvHP	 +=	(((CWeaponItem*)item)->getModAmount(MOD_CONVMPTOHP) - ((CWeaponItem*)item)->getModAmount(MOD_CONVHPTOMP));
		this->pConvMP	 += (((CWeaponItem*)item)->getModAmount(MOD_CONVHPTOMP) - ((CWeaponItem*)item)->getModAmount(MOD_CONVMPTOHP));
		this->pHPP		 += ((CWeaponItem*)item)->getModAmount(MOD_HPP);
		this->pMPP		 += ((CWeaponItem*)item)->getModAmount(MOD_MPP);

		//Elemental resistances
		this->r.fire	 += ((CWeaponItem*)item)->getModAmount(MOD_FIRERES);
		this->r.ice		 += ((CWeaponItem*)item)->getModAmount(MOD_ICERES);
		this->r.wind	 += ((CWeaponItem*)item)->getModAmount(MOD_AIRRES);
		this->r.earth	 += ((CWeaponItem*)item)->getModAmount(MOD_EARTHRES);
		this->r.thunder	 += ((CWeaponItem*)item)->getModAmount(MOD_THUNDERRES);
		this->r.water	 += ((CWeaponItem*)item)->getModAmount(MOD_WATERRES);
		this->r.light	 += ((CWeaponItem*)item)->getModAmount(MOD_LIGHTRES);
		this->r.dark	 += ((CWeaponItem*)item)->getModAmount(MOD_DARKRES);

		unlockAttributes();
		return;
	}
	CConsole::outDebOnly("Item is not a weapon or armor.");
}

unsigned long CChar::getGil() {
	return gil->getQuantity();
}

void CChar::setGil(unsigned long newGil) {
	gil->setQuantity(newGil);
	sendUpdateGil();
}

void CChar::addGil(unsigned long tmpGil) {
	unsigned long currGil;
	currGil = this->getGil();
	this->setGil(currGil + tmpGil);
}

void CChar::removeGil(unsigned long tmpGil) {
	unsigned long currGil;
	currGil = this->getGil();
	if(currGil < tmpGil)
		this->setGil(0);
	else
		this->setGil(currGil - tmpGil);
}

void CChar::sendUpdateGil() {
	unsigned long quantity = this->getGil();
	unsigned char slotID = 0x00;
	this->quePacket(CInventoryModifyPacket(slotID, LOC_INVENTORY, quantity));

	char query[8192];
	unsigned long count;
	sprintf(query,"UPDATE chars SET gil = %i where charid = %d;",this->getGil(), this->getId());
	count = (int) strlen(query);
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn
}

// Load inventory and storages on login
void CChar::loadInvStor() {
	int count;
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[8192];

	sprintf(query, "SELECT size_inv, size_mog, size_locker, size_storage FROM chars WHERE charid = %i",this->getId());
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res)
	{
		CConsole::outErr("Error looking up characters inventory sizes");
		return;
	}
	if(!mysql_num_rows(res)){
		mysql_free_result(res);
	}
	else {
		while( row = mysql_fetch_row(res) ) {
			inventory->setSize(atoi(row[0]));
			mogsafe->setSize(atoi(row[1]));
			moglocker->setSize(atoi(row[2]));
			storage->setSize(atoi(row[3]));

			inventory->loadSQL(LOC_INVENTORY);
			mogsafe->loadSQL(LOC_MOGSAFE);
			storage->loadSQL(LOC_STORAGE);
			moglocker->loadSQL(LOC_MOGLOCKER);
		}
		mysql_free_result(res);
	}
}

void CChar::updateInvStor(unsigned char locationID, unsigned char slotID) {
	switch(locationID) {
		case LOC_INVENTORY:
			inventory->updateSQL(slotID);
			break;
		case LOC_MOGSAFE:
			mogsafe->updateSQL(slotID);
			break;
		case LOC_STORAGE:
			storage->updateSQL(slotID);
			break;
		case LOC_MOGLOCKER:
			moglocker->updateSQL(slotID);
			break;
	}
}

unsigned char CChar::getSizeInvStor(unsigned char locationID) {
	unsigned char size = 0;
	switch(locationID) {
		case LOC_INVENTORY:
			size = inventory->getSize();
			break;
		case LOC_MOGSAFE:
			size = mogsafe->getSize();
			break;
		case LOC_STORAGE:
			size = storage->getSize();
			break;
		case LOC_MOGLOCKER:
			size = moglocker->getSize();
			break;
		}
	return size;
}

void CChar::increaseSizeInvStor(unsigned char locationID, unsigned char amount) {
	unsigned char size = 0;
	size = getSizeInvStor(locationID);
	unsigned char sizeRemaining = 0;
	sizeRemaining = 254 - getSizeInvStor(locationID);
	if(amount > sizeRemaining) {
		CConsole::outDebOnly("Cannot increase storage higher than 254");
		return;
	}
	size += amount;
	switch(locationID) {
		case LOC_INVENTORY:
			inventory->setSize(size);
			break;
		case LOC_MOGSAFE:
			mogsafe->setSize(size);
			break;
		case LOC_STORAGE:
			storage->setSize(size);
			break;
		case LOC_MOGLOCKER:
			moglocker->setSize(size);
			break;
	}
	CConsole::outDebOnly("Storage %i increased to %i",locationID,size);
}

unsigned char CChar::moveItemToInvStor(CItem * item, unsigned char locationID) {
	unsigned newSlotID = 0x00;
	switch(locationID) {
		case LOC_INVENTORY:
			newSlotID = inventory->moveItemTo(item);
			break;
		case LOC_MOGSAFE:
			newSlotID = mogsafe->moveItemTo(item);
			break;
		case LOC_STORAGE:
			newSlotID = storage->moveItemTo(item);
			break;
		case LOC_MOGLOCKER:
			newSlotID = moglocker->moveItemTo(item);
			break;
	}
	return newSlotID;
}


void CChar::sendInvStorAll() {
	unsigned short itemID = 0xFFFF;  // gil
	unsigned long quantity = this->getGil();
	CItem * item = new CItem;
	item->setId(itemID);
	item->setQuantity(quantity);

	unsigned char slotID = 0x00;
	this->quePacket(CInventoryItemPacket(item, LOC_INVENTORY, slotID, 0));

	for(unsigned char locationID = 0; locationID <= 5; locationID++) {
		unsigned char size = getSizeInvStor(locationID);
		for(unsigned char slotID = 1; slotID <= size; slotID++) {
			CItem * item;
			item = getItem(locationID, slotID);
			if(item != NULL) {
				if( (locationID == LOC_INVENTORY) && (slotID == equip[SLOT_LINKSHELL]) )
					this->quePacket(CInventoryItemPacket(item, locationID, slotID,true));
				else
					this->quePacket(CInventoryItemPacket(item, locationID, slotID,false));
			}
		}
	}

	for(int i=0; i<16; i++) 
	{
		CItem * item = NULL;
		item = getItem(LOC_INVENTORY, equip[i]);
		if(item != NULL) {
			unsigned short itemID = item->getId();
			unsigned long quantity = item->getQuantity();
			this->quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, equip[i], INV_NODROP));
		}
	}

	this->sendFinishInvStor();
}

void CChar::sendItemInvStor(unsigned char locationID, unsigned char slotID) {
	CItem * item;
	item = getItem(locationID, slotID);
	if (item != NULL) {
		if( (locationID == LOC_INVENTORY) && (slotID == equip[SLOT_LINKSHELL]) )
			this->quePacket(CInventoryItemPacket(item, locationID, slotID,true));
		else
			this->quePacket(CInventoryItemPacket(item, locationID, slotID,false));
	}
}

void CChar::sendUpdateInvStor(unsigned char slotID, unsigned char locationID, unsigned long quantity) {
	this->quePacket(CInventoryModifyPacket(slotID, locationID, quantity));
}

void CChar::sendFinishInvStor() {
	this->quePacket(CInventoryFinishPacket());
}

unsigned char CChar::getFreeSlotCount(unsigned char locationID) {
	unsigned char size = getSizeInvStor(locationID);
	unsigned char count = 0;
	for(unsigned char slotID = 1; slotID <= size; slotID++) {
		CItem * item = NULL;
		item = getItem(locationID, slotID);
		if(item == NULL)
			count += 1;
	}
	return count;
}

unsigned char CChar::addItem(unsigned short itemID, unsigned char locationID, unsigned long quantity){
	if(itemID == 0)
		return 0xFF;
	unsigned char newSlotID = 0x00;
	switch(locationID) {
		case LOC_INVENTORY:
			newSlotID = inventory->addItem(itemID, quantity);
			break;
		case LOC_MOGSAFE:
			newSlotID = mogsafe->addItem(itemID, quantity);
			break;
		case LOC_STORAGE:
			newSlotID = storage->addItem(itemID, quantity);
			break;
		case LOC_MOGLOCKER:
			newSlotID = moglocker->addItem(itemID, quantity);
			break;
	}
	if(newSlotID != 0xFF) {
		this->updateInvStor(locationID, newSlotID);
		this->sendItemInvStor(locationID, newSlotID);
	}
	return newSlotID;
}

CItem * CChar::getItem(unsigned char locationID, unsigned char slotID) {
	if(slotID <= 0)
		return NULL;
	CItem * item;
	switch(locationID) {
		case LOC_INVENTORY:
			item = inventory->getItem(slotID);
			break;
		case LOC_MOGSAFE:
			item = mogsafe->getItem(slotID);
			break;
		case LOC_STORAGE:
			item = storage->getItem(slotID);
			break;
		case LOC_MOGLOCKER:
			item = moglocker->getItem(slotID);
			break;
	}
	return item;
}

unsigned long CChar::getItemQuantity(unsigned char locationID, unsigned short itemID) {
	unsigned long quantity = 0;
	switch(locationID) {
		case LOC_INVENTORY:
			quantity = inventory->getItemQuantity(itemID);
			break;
		case LOC_MOGSAFE:
			quantity = mogsafe->getItemQuantity(itemID);
			break;
		case LOC_STORAGE:
			quantity = storage->getItemQuantity(itemID);
			break;
		case LOC_MOGLOCKER:
			quantity = moglocker->getItemQuantity(itemID);
			break;
	}
	return quantity;
}

CWeaponItem * CChar::getWeaponItem(unsigned char slotID) {
	CWeaponItem * item;
	item = (CWeaponItem*)getItem(LOC_INVENTORY, equip[slotID]);
	if((item==NULL) || (item->getType() != ITEM_WEAPON))
		return NULL;
	return item;
}

CArmorItem * CChar::getArmorItem(unsigned char slotID) {
	CArmorItem * item;
	item = (CArmorItem*)getItem(LOC_INVENTORY, equip[slotID]);
	if((item==NULL) || (item->getType() != ITEM_ARMOR))
		return NULL;
	return item;
}

CLinkshellItem * CChar::getLinkshellItem() {
	CLinkshellItem * item = NULL;
	item = (CLinkshellItem*)getItem(LOC_INVENTORY, equip[SLOT_LINKSHELL]);
	return item;
}

CLinkshell * CChar::getLinkshell() {
	CLinkshellItem * lsItem = getLinkshellItem();
	if(lsItem == NULL) {
		return NULL;
	}
	unsigned long lsID = lsItem->getLinkshellId();
	if(lsID > 0) {
		CLinkshell * linkshell = NULL;
		linkshell = CLinkshell::getLinkshellById(lsID);
		return linkshell;
	} else
		return NULL;
}

void CChar::setLinkshellSlotID(unsigned char slotID) {
	CLinkshell * currentLS = NULL;
	CLinkshell * newLS = NULL;

	CLinkshellItem * currentLSItem = NULL;
	currentLSItem = getLinkshellItem();
	if(currentLSItem != NULL) {
		unsigned long currentLSid = 0;
		currentLSid = currentLSItem->getLinkshellId();
		currentLS = CLinkshell::getLinkshellById(currentLSid);
	}

	CLinkshellItem * newLSItem = NULL;
	newLSItem = (CLinkshellItem*)getItem(LOC_INVENTORY, slotID);
	if(newLSItem == NULL)
		CConsole::outDebOnly("Linkshell not equiped.", slotID);
	else
		newLS = CLinkshell::getLinkshellById(newLSItem->getLinkshellId());

	if (newLS == currentLS)
		return;

	if (currentLS!=NULL)
		currentLS->removeOnlineMember(this);

	if (newLS!=NULL)
		newLS->addOnlineMember(this);

	char query[8192];
	unsigned long count;
	sprintf(query,"UPDATE char_equip SET link = %i where charid = %d;",slotID, this->getId());
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	equip[SLOT_LINKSHELL] = slotID;
}

void CChar::removeItem(unsigned char slotID, unsigned char locationID, unsigned long quantity) {
	CItem * item;
	item = getItem(locationID, slotID);
	if(item == NULL) {
		CConsole::outDebOnly("No item in slot %i", slotID);
		return;
	}
	unsigned long newQty = 0;
	switch(locationID) {
		case LOC_INVENTORY:
			inventory->removeQuantity(slotID, quantity);
			break;
		case LOC_MOGSAFE:
			mogsafe->removeQuantity(slotID, quantity);
			break;
		case LOC_STORAGE:
			storage->removeQuantity(slotID, quantity);
			break;
		case LOC_MOGLOCKER:
			moglocker->removeQuantity(slotID, quantity);
			break;
	}
	this->updateInvStor(locationID, slotID);
	item = getItem(locationID, slotID);
	if(item != NULL) {
		newQty = item->getQuantity();
		this->sendUpdateInvStor(slotID, locationID, newQty);
	} else {
		this->sendUpdateInvStor(slotID, locationID, 0);
	}	
}

void CChar::removeItemQuantity(unsigned short itemID, unsigned char locationID, unsigned long quantity) {
	unsigned char size = getSizeInvStor(locationID);
	unsigned long tempQty = getItemQuantity(locationID, itemID);
	if(quantity > tempQty)
		return;
	tempQty = quantity;
	for(unsigned char slotID = 1; slotID <= size; slotID++) {
		CItem * item;
		item = getItem(locationID, slotID);
		if(item != NULL) {
			if(item->getId() == itemID) {
				if(tempQty > item->getQuantity()) {
					removeItem(slotID, locationID, item->getQuantity());
					tempQty -= item->getQuantity();
				} else {
					removeItem(slotID, locationID, tempQty);
					return;
				}
			}
		}
	}
}

void CChar::moveItem(unsigned char fromLocationID, unsigned char fromSlotID, unsigned char toLocationID, unsigned char toSlotID, unsigned long quantity) {
	CItem * item;
	item = getItem(fromLocationID, fromSlotID);
	if(item == NULL)
		return;
	if(item->getQuantity() < quantity) {
		CConsole::outDebOnly("Trying to move too much quantity from location %i slot %i.", fromLocationID, fromSlotID);
		return;
	}
	unsigned char newSlotID = 0x00;
	unsigned long newQuantity = item->getQuantity() - quantity;
	if(newQuantity > 0) {
		// break the stack
		newSlotID = addItem(item->getId(), toLocationID, quantity);
		if(newSlotID == 0xFF) {
			CConsole::outDebOnly("Location %i full.", toLocationID);
			return;
		} else {
			item->setQuantity(newQuantity);
			this->updateInvStor(fromLocationID, fromSlotID);
			this->sendUpdateInvStor(fromSlotID, fromLocationID, newQuantity);
			CConsole::outDebOnly("Quantity %i of item at location %i slot %i moved to location %i.", quantity, fromLocationID, fromSlotID, toLocationID);
		}
	} else if(newQuantity == 0) {
		newSlotID = moveItemToInvStor(item, toLocationID);
		if(newSlotID != 0xFF) {
			item->setQuantity(quantity);
			switch(fromLocationID) {
				case LOC_INVENTORY:
					inventory->setItem(fromSlotID, NULL);
					break;
				case LOC_MOGSAFE:
					mogsafe->setItem(fromSlotID, NULL);
					break;
				case LOC_STORAGE:
					storage->setItem(fromSlotID, NULL);
					break;
				case LOC_MOGLOCKER:
					moglocker->setItem(fromSlotID, NULL);
					break;
			}
			this->updateInvStor(fromLocationID, fromSlotID);
			this->sendUpdateInvStor(fromSlotID, fromLocationID, newQuantity);
			this->updateInvStor(toLocationID, newSlotID);
			this->sendItemInvStor(toLocationID, newSlotID);
			CConsole::outDebOnly("Entire quantity of item at location %i slot %i moved to location %i.", fromLocationID, fromSlotID, toLocationID);
		} else {
			item->setQuantity(quantity);
			switch(fromLocationID) {
				case LOC_INVENTORY:
					inventory->setItem(fromSlotID, item);
					break;
				case LOC_MOGSAFE:
					mogsafe->setItem(fromSlotID, item);
					break;
				case LOC_STORAGE:
					storage->setItem(fromSlotID, item);
					break;
				case LOC_MOGLOCKER:
					moglocker->setItem(fromSlotID, item);
					break;
			}
			CConsole::outDebOnly("Location %i full.", toLocationID);
			return;
		}
	}
	sendFinishInvStor();
}

void CChar::sortInvStor(unsigned char locationID) {
	unsigned char size = getSizeInvStor(locationID);
	if(size == 0)
		return;

	for(unsigned char slotID = 1; slotID <= size; slotID++) {
		CItem * item;
		item = getItem(locationID, slotID);
		if(item != NULL) {
			for(unsigned char slotID2 = slotID+1; slotID2 <= size; slotID2++) {
				CItem * item2;
				item2 = getItem(locationID, slotID2);
				if(item2 != NULL) {
					if(item->getId() == item2->getId()) {
						unsigned long totalQty;
						totalQty = item->getQuantity() + item2->getQuantity();

						unsigned long moveQty = 0;
						if(totalQty >= item->getStackSize()) {
							moveQty = item->getStackSize() - item->getQuantity();
						} else {
							moveQty = item2->getQuantity();
						}
						if(moveQty > 0) {
							item->setQuantity(item->getQuantity() + moveQty);
							this->updateInvStor(locationID, slotID);
							this->sendUpdateInvStor(slotID, locationID, item->getQuantity());
							removeItem(slotID2, locationID, moveQty);
						}
					}
				}
			}
		}
	}
	this->sendFinishInvStor();
}

void CChar::useItem(unsigned char slotID){  

	//Double action checks.
	if ((this->m_isUsingItem != 0) || this->isCasting() || this->isHealing() || this->isRangedAttacking()) {
		return;
	}

	//Let's do it!
	CItem* item = this->getItem(LOC_INVENTORY,slotID);
	if (item == NULL) {
		return;
	}
	CConsole::outDebOnly("Found item %s %i.",item->getItemName(),item->getId());
	this->m_isUsingItem = slotID;

	//Valid targets.
	CBaseEntity * target = this->getCChar(this->m_itemTarget,"",0);
	if (target == NULL) {
		target = CZoneHandler::getInstance()->getMob(this->m_itemTarget,this->loc.zone);
	}
	if (target == NULL) {
		target = CZoneHandler::getInstance()->getNpc(this->m_itemTarget,this->loc.zone);
	}
	if (target->isNPC() || target == NULL) {
		this->quePacket(CMessagePacket(this,this,0,0,155));
        this->m_isUsingItem = 0;
        return;
    }
	if (target == NULL) {
		return;
	}
	int tmpType = target->getObjectType();
	if (target->getId() == this->getId())
		tmpType = 1;
	if (!(item->getAllowedTargets() & tmpType)) {
		this->quePacket(CMessagePacket(this,this,0,0,155));
		this->m_isUsingItem = 0;
		return;
	}

	//Now do the hand-in-pocket animation
	CActionPacket* attackPacket=new CActionPacket();

	attackPacket->setActorId(this->m_id);
	attackPacket->setCategorie(9); 
	attackPacket->setStartAnimationCategorie(0x18);
	attackPacket->setStartAnimation(0x7469);
	attackPacket->setParameter1(0x163);
	attackPacket->setUnknown(0x00000009);

	apActionEntry newEntry;
	memset(&newEntry,0,sizeof(newEntry));

	newEntry.targetAnimationID = 0x105;
	newEntry.parameter2 = item->getId();
	newEntry.messageType = 0x1c;

	attackPacket->setTargetID(1,this->m_id);
	attackPacket->setActionEntry(1,1,&newEntry);

	attackPacket->finalizePacket();
	attackPacket->savePacket();

	CChar::insertPacketGlob(CHAR_INRANGE,*attackPacket,this);

	//Now go to CharItemServ
	CreateThread(0,0,CharItemServ,this,0,0);
}

int CChar::health(unsigned char * data, unsigned short num){

	memset(data, 0, 28);
	data[0] = 0xDF;
	data[1] = 0x0E;
	memcpy(data+2, &num, sizeof(num));
	memcpy(data+4, &(this->m_id), 4);
	memcpy(data+8, &(this->h.hp), 2);
	if((this->getMaxMp()) <= 0) {
		this->h.mp = 0;
	}
	memcpy(data+12, &(this->h.mp), 2);
	memcpy(data+16, &(this->h.tp), 2);

	unsigned short targId = this->getTargId();
	memcpy(data+0x14, &targId, 2);
	data[0x16] = (unsigned char) (((float)this->getHp() / (float)(this->getMaxHp())) * 100);
	data[0x17] = (unsigned char) (((float)this->getMp() / (float)(this->getMaxMp())) * 100);
	return 28;
}

	//Returns the characters gender
unsigned char CChar::getGender(void) {
	//default is female
	unsigned char gender = 0x00;

	//If the race is male, set gender to male.
	if(this->l.race == 1 || this->l.race == 3 || this->l.race == 5 || this->l.race == 8) {
		gender = 0x01;
	}

	//Return gender
	return gender;
}

void CChar::cmjob(unsigned char job, unsigned char level){
	char query[8192];
	int count;

	unsigned char levelCap = 0x00;
	levelCap = getLevelCap();

	if(level > levelCap)
		level = levelCap;
	if (level < 1)
		level = 1;

	if(job > 0x14)
		job = 0x14;
	else if (job == 0x00)
		job = 0x01;

	this->m_lvlSub = this->j.job[this->m_jobSub];

	if(this->m_lvlSub > (level / 2)) {
		this->m_lvlSub = level / 2;
		if(this->m_lvlSub < 1)
			this->m_lvlSub = 1;
	}

	this->m_jobMain = job;
	this->m_lvlMain = level;

	CStats::calcStats(this);

	this->h.hp = (this->getMaxHp());
	this->h.mp = (this->getMaxMp());
	this->h.hpp = 0x64;

	if((this->getMaxMp()) == 0)
		this->h.mpp = 0x00;
	else
		this->h.mpp = 0x64;

	
	sprintf(query, "UPDATE char_stats SET mjob = %i, sjob = %i WHERE charid = %i",
		this->m_jobMain,this->m_jobSub,this->m_id);

	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	sprintf(query, "UPDATE char_job SET %s = %i WHERE charid = %i",
		CUtil::getJobString(this->m_jobMain),this->m_lvlMain,this->m_id);

	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	switch(this->m_jobMain) 
	{
		case 0x01:
			this->j.war = this->m_lvlMain;
			break;
	    case 0x02:
			this->j.mnk = this->m_lvlMain;
			break;
		case 0x03:
			this->j.whm = this->m_lvlMain;
			break;
		case 0x04:
			this->j.blm = this->m_lvlMain;
			break;
		case 0x05:
			this->j.rdm = this->m_lvlMain;
			break;
		case 0x06:
			this->j.thf = this->m_lvlMain;
			break;
		case 0x07:
			this->j.pld = this->m_lvlMain;
			break;
		case 0x08:
			this->j.drk = this->m_lvlMain;
			break;
		case 0x09:
			this->j.bst = this->m_lvlMain;
			break;
		case 0x0a:
			this->j.brd = this->m_lvlMain;
			break;
		case 0x0b:
			this->j.rng = this->m_lvlMain;
			break;
		case 0x0c:
			this->j.sam = this->m_lvlMain;
			break;
		case 0x0d:
			this->j.nin = this->m_lvlMain;
			break;
		case 0x0e:
			this->j.drg = this->m_lvlMain;
			break;
		case 0x0f:
			this->j.smn = this->m_lvlMain;
			break;
		case 0x10:
			this->j.blu = this->m_lvlMain;
			break;
		case 0x11:
			this->j.cor = this->m_lvlMain;
			break;
		case 0x12:
			this->j.pup = this->m_lvlMain;
			break;
		case 0x13:
			this->j.dnc = this->m_lvlMain;
			break;
		case 0x14:
			this->j.sch = this->m_lvlMain;
			break;
	}

	this->m_expForNextLevel = this->getExpForNextLevel();

	this->updateAbilities();
	this->updateHealth();
	this->quePacket(CCharStatsPacket(this));
	this->quePacket(CCharSkillPacket(this));
	this->sendLevel();
	this->updateChar();

}

void CChar::csjob(unsigned char job, unsigned char level){
	char query[8192];
	int count;

	unsigned char levelCap = 0x00;
	levelCap = getLevelCap();

	if(level > (levelCap/2) )
		level = (levelCap/2);
	if(job > 0x14)
		job = 0x14;

	if(level > (this->m_lvlMain / 2))
		level = (this->m_lvlMain / 2);
	if(level < 1)
		level = 1;

	this->m_jobSub = job;
	this->m_lvlSub = level;

	CStats::calcStats(this);

	this->h.hp = (this->getMaxHp());
	this->h.mp = (this->getMaxMp());
	this->h.hpp = 0x64;

	if((this->getMaxMp()) == 0)
		this->h.mpp = 0x00;
	else
		this->h.mpp = 0x64;

	sprintf(query, "UPDATE char_stats SET mjob = %i, sjob = %i WHERE charid = %i",
		this->m_jobMain,this->m_jobSub,this->getId());

	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	sprintf(query, "UPDATE char_job SET %s = %i WHERE charid = %i",
		CUtil::getJobString(this->m_jobSub),this->m_lvlSub,this->getId());

	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	switch(this->m_jobSub) 
	{
		case 0x01:
			if(this->j.war < this->m_lvlSub)
				this->j.war = this->m_lvlSub;
			break;
	    case 0x02:
			if(this->j.mnk < this->m_lvlSub)
				this->j.mnk = this->m_lvlSub;
			break;
		case 0x03:
			if(this->j.whm < this->m_lvlSub)
				this->j.whm = this->m_lvlSub;
			break;
		case 0x04:
			if(this->j.blm < this->m_lvlSub)
				this->j.blm = this->m_lvlSub;
			break;
		case 0x05:
			if(this->j.rdm < this->m_lvlSub)
				this->j.rdm = this->m_lvlSub;
			break;
		case 0x06:
			if(this->j.thf < this->m_lvlSub)
				this->j.thf = this->m_lvlSub;
			break;
		case 0x07:
			if(this->j.pld < this->m_lvlSub)
				this->j.pld = this->m_lvlSub;
			break;
		case 0x08:
			if(this->j.drk < this->m_lvlSub)
				this->j.drk = this->m_lvlSub;
			break;
		case 0x09:
			if(this->j.bst < this->m_lvlSub)
				this->j.bst = this->m_lvlSub;
			break;
		case 0x0a:
			if(this->j.brd < this->m_lvlSub)
				this->j.brd = this->m_lvlSub;
			break;
		case 0x0b:
			if(this->j.rng < this->m_lvlSub)
				this->j.rng = this->m_lvlSub;
			break;
		case 0x0c:
			if(this->j.sam < this->m_lvlSub)
				this->j.sam = this->m_lvlSub;
			break;
		case 0x0d:
			if(this->j.nin < this->m_lvlSub)
				this->j.nin = this->m_lvlSub;
			break;
		case 0x0e:
			if(this->j.drg < this->m_lvlSub)
				this->j.drg = this->m_lvlSub;
			break;
		case 0x0f:
			if(this->j.smn < this->m_lvlSub)
				this->j.smn = this->m_lvlSub;
			break;
		case 0x10:
			if(this->j.blu < this->m_lvlSub)
				this->j.blu = this->m_lvlSub;
			break;
		case 0x11:
			if(this->j.cor < this->m_lvlSub)
				this->j.cor = this->m_lvlSub;
			break;
		case 0x12:
			if(this->j.pup < this->m_lvlSub)
				this->j.pup = this->m_lvlSub;
			break;
		case 0x13:
			if(this->j.dnc < this->m_lvlSub)
				this->j.dnc = this->m_lvlSub;
			break;
		case 0x14:
			if(this->j.sch < this->m_lvlSub)
				this->j.sch = this->m_lvlSub;
			break;
	}

	this->updateAbilities();
	if(job != 0x0D) {  // If subjob is not ninja, check offhand
		CItem * item = getItem(LOC_INVENTORY, equip[SLOT_OFF]);
		// If offhand is a weapon, remove it
		if(item != NULL)
			if(item->getType() == ITEM_WEAPON)
				equipItem(0, SLOT_OFF);
	} 

	this->updateHealth();
	this->quePacket(CCharStatsPacket(this));
	this->quePacket(CCharSkillPacket(this));
	this->sendLevel();
	this->updateChar();
}

void CChar::debugMsg(char * buf, ...){

	CConsole::outTime(" %x %x %x ", this->getId(), this->clientConn.acctID, this->getTargId());

}

int CChar::stopDld(unsigned char * data, unsigned short num){
	int size = 0;
	
	memset(data+size, 0, 248);
	data[size] = 0x41;
	data[size+1] = 0x7c;
	memcpy(data+size+2, &num, 2);
	data[size+0xf4] = 3;
	size += 248;

	return size;
}

void CChar::spawnPCs()
{
	CChar * currChar = NULL;
	for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
		currChar = CGlobal::getCCharAt(k);
		if (currChar != NULL) {
			if((currChar->getZone() == this->getZone()) && (currChar->getId() != this->getId()) && (currChar->getId() != 0)){
				if(CUtil::distance(currChar->getPos(), this->getPos()) < 50) {
					if (this->newSpawnIdList->find(currChar->getId()) == NULL) {
						this->newSpawnIdList->pushBack(currChar->getId(),(char)0x01);
					}
					this->quePacket(CCharPacket(currChar,true));
				} else {
					if (this->newSpawnIdList->find(currChar->getId()) != NULL) {
						this->quePacket(CCharPacket(currChar,false));
						this->newSpawnIdList->removeByKey(currChar->getId());
					}
				}
			}
		}
	}
	if(this->m_pPet != NULL)
		if( (m_pPet->m_spawnTime + 2000) <= clock() ) {
			m_pPet->face(this->loc.p);
			m_pPet->moveTo(this->loc.p);
			m_pPet->sendUpdate();
		}
}

void CChar::updateHealth() {
	this->updateChar();
	if ( (this->inParty > 0) && (this->getParty() != NULL))  {
		this->updatePartyStats();
	} else {
		this->quePacket(CCharHealthPacket(this));
	}
}

int CChar::recvChat(unsigned char * data, unsigned short num, CComm * comm){

	unsigned long count=0;
	unsigned long lenght = 0;

	switch(comm->ident) {

		case 0x03:
			lenght = comm->lenght*2-20;
			count =  lenght + 21;
			memset(data, 0, count+2);
			memcpy(data+21, comm->message, lenght);
			break;

		case 0x07:
			lenght = (int) strlen(comm->pData.c);
			count =  lenght + 22;
			memset(data, 0, count+2);
			memcpy(data+21, comm->pData.uc, lenght);
			break;

		default:

			lenght = comm->lenght*2-6;
			count =  lenght + 21;
			CConsole::outTime("%s: %s ", comm->fromc, comm->message);
			//Sleep(400);
			memset(data, 0, count+2);
			memcpy(data+21, comm->message, lenght);
			break;

	}
	
	data[0] = 0x17;
	data[1] = (unsigned char)(count+(comm->ident == 3 ? 0 : 1))/2;
	memcpy(data+2, &num, sizeof(num));

	char name[16];
	memcpy(name, &comm->fromc, 16);
	CChar * fromChar = getCChar(0, name, 0);
	if(fromChar != NULL)
		if(fromChar->h.nameFlag & FLAG_GM)
			data[4] = 0x01;

	memcpy(data+5, comm->fromc, 16);
	data[20] = comm->ident;



	return (int)count;
}

int CChar::sendErr(unsigned char * data, unsigned short num, unsigned short n){
	memset(data, 0, 16);
	data[0] = 0x9;
	data[1] = 8;
	memcpy(data+2, &n, sizeof(n));
	memcpy(data+10, &num, sizeof(num));

	return 16;
}

void CChar::sendMogMenu()
{
	this->quePacket(CMogMenuPacket());
}

// Only load char's look for character select
bool CChar::loadLook(int charid, bool forLogin)
{
	lockAttributes();
	int count;
	MYSQL_RES* res;
	MYSQL_ROW row;
	char query[8192];
	unsigned long * lengths;

	memset(query, 0, sizeof(query));
	sprintf(query, "SELECT charname, charid, acctid, ip, servip, sessionkey, keyitems, spells, firstlogin, targid, pos_zone, pos_x, pos_y, pos_z, pos_prevzone, pos_rot, gil, size_inv, size_mog, size_locker, size_storage, hp_zone, hp_x, hp_y, hp_z, nation, rank_san, rank_bas, rank_win, rank_points_san, rank_points_bas, rank_points_win, fame_san, fame_bas, fame_win, fame_norg, level_cap, title, boundary FROM chars WHERE charid = %i",charid);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn2

	if(!res)
	{
		unlockAttributes();
		CConsole::outErr("Char Data not found in table 'chars'. Charid: %d", charid);
		CConsole::outDebOnly("Query: %s", query);
		return false;
	}
 
	if(row = mysql_fetch_row(res)) {
		lengths = mysql_fetch_lengths(res);

		memset(this->m_name,0,17);
		memcpy(this->m_name, row[0], lengths[0]);
		this->setId((unsigned int)atoi(row[1]));
		this->clientConn.acctID			= atoi(row[2]);

		memcpy((char *)&(this->clientConn.clientIP), row[3], lengths[3]);

		memcpy((char *)(this->enc.key), row[5], lengths[5]);
		memcpy((char *)&(this->k),row[6],lengths[6]);
		memcpy((char *)&(this->sp), row[7], lengths[7]);

		this->firstlogin		= atoi(row[8]);

		if (forLogin)
			this->setTargId(atoi(row[9]));

		this->loc.zone			= atoi(row[10]);
		//no need to unlock zone here!!!

		this->loc.p.x			= (float)atof(row[11]);
		this->loc.p.y			= (float)atof(row[12]);
		this->loc.p.z			= (float)atof(row[13]);
		this->loc.prevzone		= atoi(row[14]);
		this->loc.p.rotation	= atoi(row[15]);

		this->setGil(atoi(row[16]));

		this->st.homepoint		= atoi(row[21]);
		
		this->m_homePoint.x		= (float)atof(row[22]);
		this->m_homePoint.y		= (float)atof(row[23]);
		this->m_homePoint.z		= (float)atof(row[24]);
		this->st.n.home			= atoi(row[25]);
		this->st.n.rank[0]		= atoi(row[26]);
		if(this->st.n.rank[0] < 1)
			this->st.n.rank[0] = 1;
		this->st.n.rank[1]		= atoi(row[27]);
		if(this->st.n.rank[1] < 1)
			this->st.n.rank[1] = 1;
		this->st.n.rank[2]		= atoi(row[28]);
		if(this->st.n.rank[2] < 1)
			this->st.n.rank[2] = 1;
		this->st.n.rankpoints[0] = atoi(row[29]);
		this->st.n.rankpoints[1] = atoi(row[30]);
		this->st.n.rankpoints[2] = atoi(row[31]);
		this->fameSan			= atoi(row[32]);
		this->fameBas			= atoi(row[33]);
		this->fameWin			= atoi(row[34]);
		this->fameNorg			= atoi(row[35]);
		this->m_levelCap		= atoi(row[36]);
		this->m_currentTitle	= atoi(row[37]);
		this->boundaryId		= atoi(row[38]);
		//sem_post(&commListLockedSemaphore);
	}
	else 
	{
		CConsole::outErr("Not Found !!!");
	}

	mysql_free_result(res);

	memset(query, 0, sizeof(query));
	sprintf(query, "SELECT `face`, `race`, `head`, `body`, `hands`, `legs`, `feet`, `main`, `sub`, `range` FROM char_look where `charid` = %i",this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	
	if(!res)
	{
		CConsole::outErr("Char Data not found in table 'char_look'. Charid: %d", charid);
		CConsole::outDebOnly("Query: %s", query);
		return false;
	}
	if(row = mysql_fetch_row(res)) {
		this->l.face	= atoi(row[0]);
		this->l.race	= atoi(row[1]);
		this->l.head	= atoi(row[2]);
		this->l.body	= atoi(row[3]);
		this->l.hands	= atoi(row[4]);
		this->l.legs	= atoi(row[5]);
		this->l.feet	= atoi(row[6]);
		this->l.main	= atoi(row[7]);
		this->l.sub		= atoi(row[8]);
		this->l.range	= atoi(row[9]);
	}
	mysql_free_result(res);

	memset(query, 0, sizeof(query));
	sprintf(query, "SELECT hp, mp, animation, flags, mjob, sjob FROM char_stats WHERE charid = %i", this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res)
	{
		unlockAttributes();
		CConsole::outErr("Char Data not found in table 'char_stats'. Charid: %d", charid);
		CConsole::outDebOnly("Query: %s", query);
		return false;
	}
 
	if(row = mysql_fetch_row(res)) {
		lengths = mysql_fetch_lengths(res);

		this->h.hp			= atoi(row[0]);
		this->h.mp			= atoi(row[1]);
		this->setAnimation(atoi(row[2]));
		this->h.flags		= atoi(row[3]);
		this->m_jobMain		= atoi(row[4]);
		this->m_jobSub		= atoi(row[5]);

	}
	mysql_free_result(res);

	memset(query, 0, sizeof(query));
	sprintf(query, "SELECT unlocked, subjob, war, mnk, whm, blm, rdm, thf, pld, drk, bst, brd, rng, sam, nin, drg, smn, blu, cor, pup, dnc, sch FROM char_job where charid = %i",this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	
	if(!res)
	{
		unlockAttributes();
		CConsole::outErr("Char Data not found in table 'char_job'. Charid: %d", charid);
		CConsole::outDebOnly("Query: %s", query);
		return false;
	}

	if(row = mysql_fetch_row(res)) {
		this->j.unlocked = atoi(row[0]);
		this->j.reserved = 0;
		this->m_subjobUnlocked = atoi(row[1]);
		this->j.war = atoi(row[2]);
		if(this->j.war > m_levelCap)
			this->j.war = m_levelCap;
		this->j.mnk = atoi(row[3]);
		if(this->j.mnk > m_levelCap)
			this->j.mnk = m_levelCap;
		this->j.whm = atoi(row[4]);
		if(this->j.whm > m_levelCap)
			this->j.whm = m_levelCap;
		this->j.blm = atoi(row[5]);
		if(this->j.blm > m_levelCap)
			this->j.blm = m_levelCap;
		this->j.rdm = atoi(row[6]);
		if(this->j.rdm > m_levelCap)
			this->j.rdm = m_levelCap;
		this->j.thf = atoi(row[7]);
		if(this->j.thf > m_levelCap)
			this->j.thf = m_levelCap;
		this->j.pld = atoi(row[8]);
		if(this->j.pld > m_levelCap)
			this->j.pld = m_levelCap;
		this->j.drk = atoi(row[9]);
		if(this->j.drk > m_levelCap)
			this->j.drk = m_levelCap;
		this->j.bst = atoi(row[10]);
		if(this->j.bst > m_levelCap)
			this->j.bst = m_levelCap;
		this->j.brd = atoi(row[11]);
		if(this->j.brd > m_levelCap)
			this->j.brd = m_levelCap;
		this->j.rng = atoi(row[12]);
		if(this->j.rng > m_levelCap)
			this->j.rng = m_levelCap;
		this->j.sam = atoi(row[13]);
		if(this->j.sam > m_levelCap)
			this->j.sam = m_levelCap;
		this->j.nin = atoi(row[14]);
		if(this->j.nin > m_levelCap)
			this->j.nin = m_levelCap;
		this->j.drg = atoi(row[15]);
		if(this->j.drg > m_levelCap)
			this->j.drg = m_levelCap;
		this->j.smn = atoi(row[16]);
		if(this->j.smn > m_levelCap)
			this->j.smn = m_levelCap;
		this->j.blu = atoi(row[17]);
		if(this->j.blu > m_levelCap)
			this->j.blu = m_levelCap;
		this->j.cor = atoi(row[18]);
		if(this->j.cor > m_levelCap)
			this->j.cor = m_levelCap;
		this->j.pup = atoi(row[19]);
		if(this->j.pup > m_levelCap)
			this->j.pup = m_levelCap;
		this->j.dnc = atoi(row[20]);
		if(this->j.dnc > m_levelCap)
			this->j.dnc = m_levelCap;
		this->j.sch = atoi(row[21]);
		if(this->j.sch > m_levelCap)
			this->j.sch = m_levelCap;
		this->m_lvlMain = atoi(row[this->m_jobMain+1]);
		if(m_lvlMain > m_levelCap)
			m_lvlMain = m_levelCap;

		if(this->m_jobSub != 0) {
			this->m_lvlSub  = atoi(row[this->m_jobSub+1]);
			if(this->m_lvlSub > (this->m_lvlMain/2))
				this->m_lvlSub = (this->m_lvlMain/2);
			if(this->m_lvlSub < 1)
				this->m_lvlSub = 1;
		}
	}
	mysql_free_result(res);

	unlockAttributes();
	return true;
}

// load a char
int CChar::loadChar(int charid, bool forLogin) 
{
	int count;
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[8192];
	if (charid == 0) 
	{
		return 0;
	}
	if(!loadLook(charid, forLogin))
		return 0;
	
	loadQuestMissionLog();
	loadInvStor();
	lockAttributes();

	this->m_objType = OBJ_PC;
	this->m_expForNextLevel = this->getExpForNextLevel();

	for(int id = 1; id < 64; id++) {
		memset(query, 0, 2000);
		count = sprintf(query, "SELECT skillvalue, rank FROM char_skills WHERE charid = %i AND skillid = %i", this->getId(), id);
		//CConsole::outTime("%s\n",query); //DEBUG

		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);

		res = mysql_store_result(mysqlConn);
		CDb::pushConn(mysqlConn); //release conn

		if (res) 
		{
			if(row = mysql_fetch_row(res)) 
			{
				this->s.skillId[id] = atoi(row[0]);
				// Can be used for weapons to avoid double SQL query for maxSkill
				this->s.rank[id] = (unsigned char)atoi(row[1]);
			} 
			else 
			{
				this->s.skillId[id] = 0;
				this->s.rank[id] = 0;

			}
			mysql_free_result(res);
		}
	}

	CStats::calcStats(this);


	memset(query, 0, 2000);
	sprintf(query, "SELECT main, sub, ranged, ammo, head, body, hands, legs, feet, neck, waist, ear1, ear2, ring1, ring2, back, link FROM char_equip where charid = %i",this->getId());
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	
	if(!res)
	{
		unlockAttributes();
		return 0;
	}

	if(row = mysql_fetch_row(res)) 
	{
		for(int equipSlotID=0; equipSlotID < 16; equipSlotID++) 
		{
			this->equip[equipSlotID] = atoi(row[equipSlotID]);
			this->applyStatMod(equipSlotID);
		}
		equip[SLOT_LINKSHELL] = 0;
		char newLinkshellSlot = atoi(row[16]);
		this->setLinkshellSlotID(newLinkshellSlot);
	}

	mysql_free_result(res);


	memset(query, 0, 2000);
	sprintf(query, "SELECT `exp_%s` FROM char_exp WHERE charid = %i",CUtil::getJobString(this->m_jobMain),this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res)
	{
		unlockAttributes();
		return 0;
	}

	if(row = mysql_fetch_row(res)) 
	{
		this->m_expCurrent = atoi(row[0]);
	}

	unlockAttributes();
	mysql_free_result(res);

	return 1;
}

// saves a new char to DB
int CChar::createChar() {
	int count;
	char query[8096];
	unsigned short fl = 0x01;
	MYSQL_RES * res;
	MYSQL_ROW row;


	memset(query,0,8096);
	//remove duplicate characters
	sprintf(query, "DELETE FROM chars WHERE charid = %i",this->getId());
	count = (int) strlen(query);
	
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "DELETE FROM char_look WHERE charid = %i",this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "DELETE FROM char_stats WHERE charid = %i",this->getId());
	count = (int) strlen(query);
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "DELETE FROM char_equip WHERE charid = %i",this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "DELETE FROM char_job WHERE charid = %i",this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "DELETE FROM char_exp WHERE charid = %i",this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	strcpy(query, "SELECT MAX(targid) FROM chars");

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, (unsigned long)strlen(query));

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	short targid = 0x400;
	if(res)
	{
		if(mysql_num_rows(res)){
			row = mysql_fetch_row(res);
			if(row[0]){
				targid = atoi(row[0]);
				targid++;
			}
		}
		mysql_free_result(res);
	}
	this->setTargId(targid);

	sprintf(query, "INSERT INTO chars (udate, acctid, charid, targid, charname, firstlogin, boundary, pos_x, pos_y, pos_z, pos_zone, pos_prevzone, pos_rot, gil, size_inv, size_mog, size_locker, size_storage, hp_zone, hp_x, hp_y, hp_z, nation, rank_san, rank_bas, rank_win, rank_points_san, rank_points_bas, rank_points_win, fame_san, fame_bas, fame_win, fame_norg, level_cap, title, ip, servip, sessionkey, keyitems, spells)");
	sprintf(query, "%s VALUES (CURRENT_TIMESTAMP,%i,%i,%i,'%s',%i,%i,%f,%f,%f,%i,%i,%i,%i,%i,%i,%i,%i,%i,%f,%f,%f,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,'",query,
		this->clientConn.acctID,this->getId(),this->getTargId(), this->m_name, fl,this->boundaryId,this->loc.p.x,this->loc.p.y,this->loc.p.z,this->loc.zone,this->loc.prevzone,(unsigned char)this->loc.p.rotation,this->getGil(),inventory->getSize(),mogsafe->getSize(),moglocker->getSize(),storage->getSize(),this->st.homepoint,this->m_homePoint.x,this->m_homePoint.y,this->m_homePoint.z,this->st.n.home,this->st.n.rank[0],this->st.n.rank[1],this->st.n.rank[2],this->st.n.rankpoints[0],this->st.n.rankpoints[1],this->st.n.rankpoints[2],this->fameSan,this->fameBas,this->fameWin,this->fameNorg,this->m_levelCap,this->m_currentTitle);
	count = (int) strlen(query);

	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->clientConn.clientIP), sizeof(unsigned int));		//ip
	strcpy(query+count, "', '");
	count += 4;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->clientConn.servIP), sizeof(unsigned int));	//servip
	strcpy(query+count, "', '");
	count += 4;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->enc.key), 20);			//sessionkey
	strcpy(query+count, "', '");
	count += 4;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->k), 512);				//keyitems
	strcpy(query+count, "', '");
	count += 4;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->sp), 256);				//spells
	strcpy(query+count, "')");
	count += 2;

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "INSERT INTO char_stats (charid, hp,  mp,  animation, flags, mjob, sjob) VALUES (%i,%i,%i,%i,%i,%i,%i)",
			this->getId(), this->getHp(), this->getMp(), this->getAnimation(), this->h.flags,this->m_jobMain,this->m_jobSub);
	count = (int) strlen(query);
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "INSERT INTO char_exp (charid) VALUES (%i)",
			this->getId());
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "INSERT INTO char_look (`charid`, `face`, `race`, `head`, `body`, `hands`, `legs`, `feet`, `main`, `sub`, `range`) VALUES (%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)",
		this->getId(), this->l.face, this->l.race, this->l.head, this->l.body, this->l.hands, this->l.legs,this->l.feet,this->l.main,this->l.sub,this->l.range);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "INSERT INTO char_equip (`charid`, `main`, `sub`, `ranged`, `ammo`, `head`, `body`, `hands`, `legs`, `feet`, `neck`, `waist`, `ear1`, `ear2`, `ring1`, `ring2`, `back`, `link`) VALUES (%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)",
		this->getId(), this->equip[0], this->equip[1], this->equip[2], this->equip[3], this->equip[4], this->equip[5],this->equip[6],this->equip[7],this->equip[8],this->equip[9],this->equip[10],this->equip[11],this->equip[12],this->equip[13],this->equip[14],this->equip[15],this->equip[16]);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	sprintf(query, "INSERT INTO char_job (charid, unlocked, subjob, war, mnk, whm, blm, rdm, thf, pld, drk, bst, brd, rng, sam, nin, drg, smn, blu, cor, pup, dnc, sch) VALUES (%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)",
		this->getId(), this->j.unlocked, this->m_subjobUnlocked, this->j.war, this->j.mnk, this->j.whm, this->j.blm,this->j.rdm,this->j.thf,this->j.pld,this->j.drk,this->j.bst,this->j.brd,this->j.rng,this->j.sam,this->j.nin,this->j.drg,this->j.smn,this->j.blu,this->j.cor,this->j.pup,this->j.dnc,this->j.sch);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	this->setCharVar("firstLogin", 1);

	return 0;
}

// write char to DB
void CChar::writeChar() {
	MYSQL_RES * res;

	int count;
	char query[8096];

	memset(query,0,8096);
	count = sprintf(query, "UPDATE chars SET udate = CURRENT_TIMESTAMP, acctid = %i, targid = %i, charname = '%s', firstlogin = %i, boundary = %i,pos_x = %f, pos_y = %f, pos_z = %f, pos_zone = %i, pos_prevzone = %i, pos_rot = %i, gil = %i, size_inv = %i, size_mog = %i, size_locker = %i, size_storage = %i, hp_zone = %i, hp_x = %f, hp_y = %f, hp_z = %f, nation = %i, rank_san = %i, rank_bas = %i, rank_win = %i, rank_points_san = %i, rank_points_bas = %i, rank_points_win = %i, fame_san = %i, fame_bas = %i, fame_win = %i, fame_norg = %i, ip = '",
		this->clientConn.acctID,this->getTargId(), this->m_name, 0,this->boundaryId,this->loc.p.x,this->loc.p.y,this->loc.p.z,this->loc.zone,this->loc.prevzone,this->loc.p.rotation,this->getGil(),inventory->getSize(),mogsafe->getSize(),moglocker->getSize(),storage->getSize(),this->st.homepoint,this->m_homePoint.x,this->m_homePoint.y,this->m_homePoint.z,this->st.n.home,this->st.n.rank[0],this->st.n.rank[1],this->st.n.rank[2],this->st.n.rankpoints[0],this->st.n.rankpoints[1],this->st.n.rankpoints[2],this->fameSan,this->fameBas,this->fameWin,this->fameNorg);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->clientConn.clientIP), sizeof(unsigned int));		//ip
	strcpy(query+count, "', servip = '");
	count += 13;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->clientConn.servIP), sizeof(unsigned int));	//servip
	strcpy(query+count, "', sessionkey = '");
	count += 17;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->enc.key), 20);			//sessionkey
	strcpy(query+count, "', keyitems = '");
	count += 15;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->k), 512);				//keyitems
	strcpy(query+count, "', spells = '");
	count += 13;
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->sp), 256);				//spells

	count += sprintf(query+count, "' WHERE charid = %i",this->getId());

	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	count = sprintf(query, "UPDATE char_stats SET hp = %i, mp = %i, animation = %i, flags = %i, mjob = %i, sjob = %i WHERE charid = %i",
				this->getHp(), this->getMp(), this->getAnimation(), this->h.flags,this->m_jobMain,this->m_jobSub,this->getId());
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	count = sprintf(query, "UPDATE char_look SET `face` = %i, `race` = %i, `head` = %i, `body` = %i, `hands` = %i, `legs` = %i, `feet` = %i, `main` = %i, `sub` = %i, `range` = %i WHERE charid = %i",
				this->l.face, this->l.race, this->l.head, this->l.body, this->l.hands, this->l.legs,this->l.feet,this->l.main,this->l.sub,this->l.range,this->getId());
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	count = sprintf(query, "UPDATE char_equip SET main = %i, sub = %i, ranged = %i, ammo = %i, head = %i, body = %i, hands = %i, legs = %i, feet = %i, neck = %i, waist = %i, ear1 = %i, ear2 = %i, ring1 = %i, ring2 = %i, back = %i, link = %i WHERE charid = %i",
		this->equip[0], this->equip[1], this->equip[2], this->equip[3], this->equip[4], this->equip[5],this->equip[6],this->equip[7],this->equip[8],this->equip[9],this->equip[10],this->equip[11],this->equip[12],this->equip[13],this->equip[14],this->equip[15],this->equip[16], this->getId());
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	count = sprintf(query, "UPDATE char_job SET unlocked = %i, subjob = %i, war = %i, mnk = %i, whm = %i, blm = %i, rdm = %i, thf = %i, pld = %i, drk = %i, bst = %i, brd = %i, rng = %i, sam = %i, nin = %i, drg = %i, smn = %i, blu = %i, cor = %i, pup = %i, dnc = %i, sch = %i WHERE charid = %i",
				this->j.unlocked, this->m_subjobUnlocked, this->j.war, this->j.mnk, this->j.whm, this->j.blm,this->j.rdm,this->j.thf,this->j.pld,this->j.drk,this->j.bst,this->j.brd,this->j.rng,this->j.sam,this->j.nin,this->j.drg,this->j.smn,this->j.blu,this->j.cor,this->j.pup,this->j.dnc,this->j.sch,this->getId());
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	for(int id = 1; id < 63; id++) {
		if(this->s.skillId[id] > 0) {
			memset(query,0,8096);
			count = sprintf(query, "SELECT skillvalue, rank FROM char_skills WHERE charid = %i AND skillid = %i", this->getId(), id);
			//CConsole::outTime("%s\n",query); //DEBUG

			MYSQL* mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, count);

			res = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn

			if (res) 
			{
				if(!mysql_num_rows(res))
				{
					memset(query,0,8096);
					count = sprintf(query, "INSERT INTO char_skills (charid, skillid, skillvalue, rank) VALUES (%i,%i,%i,%i)", this->getId(), id, this->s.skillId[id], this->s.rank[id]);
					//CConsole::outTime("%s\n",query); //DEBUG
					MYSQL* mysqlConn=CDb::getConn(); //get conn
					mysql_real_query(mysqlConn, query, count);
					CDb::pushConn(mysqlConn); //release conn
				}
				else {
					memset(query,0,8096);
					count = sprintf(query, "UPDATE char_skills SET skillvalue = %i, rank = %i WHERE charid = %i AND skillid = %i",this->s.skillId[id], this->s.rank[id], this->getId(), id);
					//CConsole::outTime("%s\n",query); //DEBUG
					MYSQL* mysqlConn=CDb::getConn(); //get conn
					mysql_real_query(mysqlConn, query, count);
					CDb::pushConn(mysqlConn); //release conn
				}
				mysql_free_result(res);
			}
		}
	}

	memset(query,0,8096);
	count = sprintf(query, "UPDATE char_exp SET `exp_%s` = %i WHERE charid = %i",
	CUtil::getJobString(this->m_jobMain),this->m_expCurrent,this->getId());
	
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	memset(query,0,8096);
	count = sprintf(query, "UPDATE char_job SET `%s` = %i WHERE charid = %i",
	CUtil::getJobString(this->m_jobMain),this->m_lvlMain,this->getId());

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn
}

void CChar::check(int charid){

	CBaseEntity * target = this->m_pTarget;

	if(target == NULL) {
		return;
	}

	if(target->getObjectType() == OBJ_MOB) {
		
		int baseExp = ((CNpc*)target)->getRealBaseExp(this->m_lvlMain);

		CConsole::outTime("DEBUG BaseExp = %i", baseExp);

		// prepare the check result packet
		// "impossible to gauge" when NM
		if(((CNpc*)target)->m_isNm) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x00, 0xf9));
			return;
		}

		// else based on baseExp
		if(baseExp >= 400) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x46, 0xAE));
		} else if(baseExp >= 240) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x45, 0xAE));
		} else if(baseExp >= 120) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x44, 0xAE));
		} else if(baseExp == 100) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x43, 0xAE));
		} else if(baseExp >= 75) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x42, 0xAE));
		} else if(baseExp >= 15) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x41, 0xAE));
		} else if(baseExp == 0) {
			this->quePacket(CMessagePacket(this, ((CNpc*)target), 0x00, 0x40, 0xAE));
		}
		return;
	} else {
		CChar * schar = CChar::getCChar(charid, "",0);
		if(schar != NULL) {
			schar->quePacket(CStandardMessagePacket(this, CHECK_MESSAGE));
			this->quePacket(CBazaarMessagePacket(schar));
			this->quePacket(CCheckPacket(this, (schar)));
		}
	}




		
/*
	//CChar * tmpChar = new CChar();
	CComm * comm = new CComm();
	comm->setComType(0x09);
	sprintf((char *)comm->fromc, "string2 %s ", this->m_name);
	comm->ident = 0x59;
	unsigned int id;
	memcpy(&id, in+4, sizeof(unsigned int));
	CChar::getCChar(id, "",0)->addCommList(*comm);

	return size;*/
}
/*
int CChar::stdmRecv(unsigned char * data, CComm * comm, unsigned short num){

	memset(data, 0, 48);
	data[0] = 0x09;
	data[1] = 0x30;
	memcpy(data+2, &num, sizeof(num));
	data[10]= comm->ident;

	//if the message is a check message add the "string2 <name>"
	if((comm->ident == 0x59)||(comm->ident == 0x58)) 
	{
		strcpy((char*)(data+13), (const char *)comm->fromc);
	}

	return 48;
}
*/
int CChar::emote(unsigned char * data, char * in, unsigned short num){

	char emote[22];

	memcpy(emote, in, 16);
	unsigned short targId = this->getTargId();
	memcpy(emote+16, &targId, 2);
	memcpy(emote+18, &(this->m_id), 4);
				
	CComm comm;
	comm.setComType(0x5d);
	comm.ident = 0x03;
	memcpy(comm.emote, emote, 22);

	CWeaponItem * weapon = this->getWeaponItem(SLOT_MAIN);
	if (weapon != NULL) {
		unsigned short itemId = weapon->getId();
		memcpy(comm.emote+22, &itemId, 2);
	}
	comm.charid = this->m_id;

	memcpy(comm.fromc, this->m_name, 17);
	this->insertComm(CHAR_INRANGE_SELF, comm, false);


	return 0;
}

int CChar::emoteRecv(unsigned char * data, CComm * comm, unsigned short num){
	int size = 0;
	int item = 0;

	memset(data+size, 0, 24);
	data[size] = 0x5A;
	data[size+1] = 0x0C;
	memcpy(data+size+2, &num, sizeof(num));
	memcpy(data+size+4,  comm->emote+18,  4);
	memcpy(data+size+8,   comm->emote+4,   4);
	memcpy(data+size+12,  comm->emote+16,  2);
	memcpy(data+size+14,  comm->emote+8,   3);

	memcpy(&item,comm->emote+22,2);
	if(item == 0x40ee) {
		comm->emote[23] = 0x47;
		comm->emote[22] = 0x6A;
	}	
	memcpy(data+size+18,  comm->emote+22,   2);

	data[size+22] = comm->emote[11];

	size += 24;


	return size;
}

void CChar::sendServerMessage() {
	quePacket(CServerMessagePacket());
}


int CChar::updatePCs(unsigned char * data, CComm * comm, unsigned short num)
{
	this->lockAttributes();
	int size = 0;
	CChar * schar = (CChar*) comm->ptr;

	/*
	unsigned short targ = 0;

	if(schar->m_pTarget != NULL) {
		targ = schar->m_pTarget->getTargId();
	} 

	targ *=2;
	*/

	memset(data+size, 0, 96+40);
	data[size] = 0x0d;
	data[size+1] = 0x2a;
	memcpy(data+size+2, &num, sizeof(num));
	memcpy(data+size+4, &(schar->m_id), sizeof(schar->m_id));
	unsigned short targId = schar->getTargId();
	memcpy(data+size+8, &targId, 2);
	data[size+0x0A] = 0x20;
	size += 84;
	this->unlockAttributes();
	return size;
}



// currently broken and needs to be ported to the current packetsystem
int CChar::diceroll(unsigned char * data, unsigned short num){
	int n;
	char number[4];
	 
	srand(clock());
	n=1+(int)(rand()%1000); 
	_itoa(n, number, 10);

	CComm * comm = new CComm();
	comm->setComType(0x09);
	comm->ident = 0x58;
	sprintf((char *)comm->fromc, "string2 %s string3 %s", this->m_name, number);
	this->insertComm(CHAR_INRANGE_SELF, *comm, false);
	

	return 0;
}

void CChar::tsit(unsigned char * data, char * in, unsigned short num){

	if(this->getAnimation() == 0x2f)
		this->setAnimation(0);
	else
		this->setAnimation(0x2f);

	this->updateChar();
}


void CChar::updateChar(){
	this->quePacket(CUpdateCharPacket(this));
}

void CChar::sendCharUpdate(){
	// this needs to be changes to a 0x0d packet at some point
	this->insertPacket(CHAR_INZONE_SELF, CUpdateCharPacket(this));
}

void CChar::sendLevel(){
	
	this->quePacket(CSixtySevenPacket(this));
	
}

unsigned char CChar::getDisplayZone(){
	unsigned char zone;

	if( (this->loc.zone==(unsigned char)0xD6) ||
		(this->loc.zone==(unsigned char)0xD7) ||
		(this->loc.zone==(unsigned char)0xD8) ||
		(this->loc.zone==(unsigned char)0xD9) ||
		(this->loc.zone==(unsigned char)0xDA) ||
		(this->loc.zone==(unsigned char)0xFD) ||
		(this->loc.zone==(unsigned char)0xFE) ||
		(this->loc.zone==(unsigned char)0xFF) )
	{
		zone = this->loc.prevzone;
	} else {
		zone = this->loc.zone;
	}
	return zone;
}


int CChar::seekAnon(unsigned char * data, char * in, unsigned short num){
	int type;

	if(in[4] == 0x01)
		this->h.flags ^= FLAG_INVITE;
	else {
		this->h.flags ^= FLAG_ANON;

		if(this->h.flags & FLAG_ANON)
			type = 0xAF;
		else
			type = 0xB0;

		CComm * comm = new CComm();
		comm->setComType(0x09);
		comm->ident = type;
		comm->charid = this->getId();
		this->addCommList(*comm);
	}

	this->updateChar();
	return 0;
}

void CChar::updateInvSize(){
	quePacket(CInventorySizePacket(this));
}

/*int CChar::cmJobMog(unsigned char * data,  char * in, unsigned short num){
	char query[8192];
	int count;

	if(in[4])
		this->m_jobMain = in[4];
	if(in[5])
		this->m_jobSub = in[5];

	if(this->m_jobMain > 0x14)
		this->m_jobMain = 0x14;
	if(this->m_jobSub > 0x14)
		this->m_jobSub = 0x14;

	CStats::calcStats(this);

	this->h.hp = (this->getMaxHp());
	this->h.mp = (this->getMaxMp());
	this->h.hpp = 0x64;

	if((this->getMaxMp()) == 0)
		this->h.mpp = 0x00;
	else
		this->h.mpp = 0x64;

	strcpy(query, "UPDATE charstats SET stat = '");
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->st), sizeof(this->st));
	strcpy(query+count, "' WHERE charid = '");
	count += (int) strlen(query+count);
	count += mysql_real_escape_string(mysqlConn, query+count, (char *)&(this->m_id), sizeof(unsigned int));
	query[count++] = '\'';

	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	this->quePacket(CCharStatsPacket(this));
	this->quePacket(CCharSkillPacket(this));
	
	return  0;
}
*/

int CChar::reqChar(unsigned char * data,  char * in, unsigned short num){
	int size = 0;
	unsigned short intarg;

	memcpy(&intarg, in+4, 2);
	unsigned short targ = 0;
	if(this->m_pTarget != NULL) {
		unsigned short targ = this->m_pTarget->getTargId();
	}
	targ *=2;

	if(intarg == this->getTargId()) {
		CConsole::outTime("Self");
		/*memset(data+size, 0, 96);
		data[size]   = 0x0d;
		data[size+1] = 0x30;
		memcpy(data+size+2, &num, sizeof(num));
		memcpy(data+size+4, &(this->m_id), 4);
		unsigned short targId = this->getTargId();
		memcpy(data+size+8, &targId, 2);
		data[size+0x0A] = 0x1f;
	
		data[size+0x0B] = (unsigned char)this->loc.p.rotation;
		memcpy(data+size+0x0C, &(this->loc.p.x), sizeof(float)*3);
		memcpy(data+size+0x1a, &targ, sizeof(targ));
		data[size+0x18] = 0x02;
		data[size+0x1C] = this->m_speed;
		data[size+0x1D] = this->m_speedAlt;
		//data[size+0x1F] = this->getAnimation();
		data[size+0x1F] = 0x00;
		data[size+0x29] = 0x01;
		int status = ((this->h.flags & 0xff000000) >> 5) + ((this->h.flags & 0x000000ff) << 7);
		memcpy(data+size+0x20, &(status), sizeof(int));
		memcpy(data+size+0x3E, &(this->l), sizeof(this->l));
		memcpy(data+size+0x40, &(this->l.head), 2);
		memcpy(data+size+0x42, &(this->l.body), 2);
		memcpy(data+size+0x44, &(this->l.hands), 2);
		memcpy(data+size+0x46, &(this->l.legs), 2);
		memcpy(data+size+0x48, &(this->l.feet), 2);
		memcpy(data+size+0x4A, &(this->l.main), 2);
		memcpy(data+size+0x4C, &(this->l.sub), 2);
		memcpy(data+size+0x4E, &(this->l.range), 2);
		memcpy(data+size+0x50, &(this->m_name), 16);
		size += 96;*/
		this->quePacket(CCharPacket(this,true));
	}
	else {

		CNpc * npc = NULL;
		unsigned int j;
		for(j = 0; j < CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].size(); j++) {
			npc = (CZoneHandler::getInstance()->npcZoneList[this->getDisplayZone()].at(j));
			//CConsole::outTime("%x ,%x, %x ", npc->getTargId(),this->getDisplayZone(), intarg);
			if(npc->getTargId() == intarg) {
				break;
			}
		}

		if (npc == NULL)
		{
			CConsole::outErr("TARGET NPC IS NULL");
			return 0;
		}
		
		if(npc->getTargId() != intarg) 
		{
			// this is nothing but a hack to send dummy npcs for missing targetids
			// we need to find out more about this.
			// Very very bad.
			CConsole::outErr("NPC with targid %d not found in zone %d",intarg,this->getDisplayZone());

			npc = new CNpc();

			npc->setTargId(intarg);
			char tmpID[4];
			memcpy(tmpID,&npc->m_id,4);
			tmpID[0] = (char)intarg;
			memcpy(&npc->m_id,tmpID,4);
			npc->type = 0x1f;
			this->quePacket(CNpcPacket(npc));
		}

		this->quePacket(CNpcPacket(npc));

	}
	return size;
}


// is a char in the specefied zone?
bool CChar::isCharInZone(unsigned char zone) {
	CChar *currChar;
	bool retVal=false;
	
	CGlobal::waitCharList(); //Wait for the char list to become free.
	for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
		currChar = CGlobal::getCCharAt(k);
		if (currChar != NULL) {
			if(currChar->getZone() == zone){
				retVal=true;
				break;
			}
		}
	}
	CGlobal::exitCharList();
	return retVal;
}

void CChar::checkChars(void) 
{
	CChar *currChar;

	CGlobal::waitCharList();
	unsigned int k = 0;

	std::vector<CChar*> removedChars;
	while(k < CGlobal::getCCharListSize())
	{
		currChar = CGlobal::getCCharAt(k);
		if (currChar == NULL) {
			continue;
		}
		CConsole::outDebOnly("CChar::checkChars: %d/%d: '%s'",k+1,CGlobal::getCCharListSize(),currChar->m_name);
		// Check to see if our last update was more than 120 seconds (2 minutes)
		if ( (((long long)clock() - currChar->lastUpdate) > 60000) )
		{
			CConsole::outTime("%s has timed out due to lack of updates.", currChar->m_name);

			// Make removal known to other clients
			//CComm comm;
			//comm.setComType(0x101);
			//comm.ptr = (LPVOID *)currChar;
			//currChar->insertComm(CHAR_INZONE, comm, true);
			currChar->insertPacket(CHAR_INZONE,CCharPacket(currChar,false));
			// Force leave a party; no matter what
			currChar->forcePartyLeave();

			if (currChar->hasTreasurePool()) {
				currChar->getTreasurePool()->removeMember(currChar);
			}

			// Remove the char from linkshell
			if (currChar->getLinkshell()!=NULL)
				currChar->getLinkshell()->removeOnlineMember(currChar);

			// Remove the character from ALL global lists
			CGlobal::removeCChar(currChar);

			if (currChar->loggedIn)
				CZoneHandler::getInstance()->decreaseZoneCounter(currChar->loc.zone);

			currChar->loggedOut=true;
			removedChars.push_back(currChar);
			/*
			//DO NOT ENABLE THIS, JUST HERE TO HAVE IT IN THE SVN LOG!
			CGlobal::waitCharRemovalList(); //TimerServ locked here
			CGlobal::charRemovalList.push_back(currChar);
			CGlobal::exitCharRemovalList();
			*/
		} if ( (((long long)clock() - currChar->lastUpdate) >= 10000) ) {
			//add the DC flag to the dcing user
			currChar->h.nameFlag |= FLAG_DC;
			k++;
		}
		else
		{
			//remove the DC flag just incase
			if (currChar->h.nameFlag & FLAG_DC) {
				currChar->h.nameFlag ^= FLAG_DC;
			}
			k++;
		}
	}
	CGlobal::exitCharList();

	CGlobal::waitCharRemovalList();
	while(removedChars.size()>0)
	{
		currChar = removedChars.at(0);
		removedChars.erase(removedChars.begin());
		CGlobal::charRemovalList.push_back(currChar);
	}
	CGlobal::exitCharRemovalList();
}

int CChar::getMaxHp() { //Actually sets Max HP

	//Base amount
	double hp = ((int)(this->m_maxHp + this->pHP));

	//Add % HP
	hp *= (1+(((double)(this->pHPP))/100));

	hp += this->m_modHPP;

	//Cap it.
	if (hp < 1) hp = 1;

	return (int)hp;
}

int CChar::getMaxMp() { //Actually sets Max MP

	//Base amount
	double mp = ((int)(this->m_maxMp + this->pMP));

	//Add % MP
	mp *= (1+(((double)(this->pMPP))/100));


	mp += this->m_modMPP;

	//Cap it
	if (mp < 0) mp = 0;

	return (int)mp;
}

bool CChar::hasKeyItem(int keyItemId) {

	if(((int)this->k.keyList[keyItemId/8]) & (int)pow((double)2,(int)(keyItemId % 8))) {
		return true;
	}
	return false;
}

bool CChar::seenKeyItem(int keyItemId) {

	if(((int)this->k.seenList[keyItemId/8]) & (int)pow((double)2,(int)(keyItemId % 8))) {
		return true;
	}
	return false;
}

bool CChar::hasSpell(int spellId) { 
	if(((int)this->sp.spellList[spellId/8]) & (int)pow((double)2,(int)(spellId % 8))) 
		return true;
	return false;
}

int CChar::addCommList(CComm comm) 
{
	lockCommList();
	this->commList.push_back(comm);
	unlockCommList();
	return 0;
}

int CChar::quePacket(CBasicPacket pack) {
	this->packetList->lock();
	CBasicPacket* copiedPacket = new CBasicPacket(pack);
	char packetKey[50];

	bool added = false;

	long long identifier = copiedPacket->getPacketIdentifier();

	unsigned int cnt = 0;

	while(!added)
	{
		//if the packet has no identifier (== it can be in the queue multiple times)
		//determine random identifier
		
		//Packets that need a specific order and are not unique
		if (identifier == 0)
		{
			struct __timeb64 currSysTime;
			_ftime64(&currSysTime);

			sprintf(packetKey,"%.2X_0_%.8X_%.4X_%.2X",copiedPacket->getDontProcessOnZoneValue(), (unsigned int)currSysTime.time, currSysTime.millitm, cnt);
		}
		else
		{
			//generate the key
			sprintf(packetKey,"%.2X_1_%.2X_%.16X",copiedPacket->getDontProcessOnZoneValue(), pack.getType(), identifier);
		}

		//try to add the packet to the list
		added = (this->packetList->pushBack(CStringKeyCS(packetKey),copiedPacket) == 0);

		//set the identifer to 0 (this enables a retry with a different random identifier for packets without an identifier)
		identifier = 0;

		//if the packet was already in the list
		//and has an identifier, copy the data
		if (!added)
		{
			if (copiedPacket->getPacketIdentifier() == 0)
			{
				cnt++;
				//CConsole::outErr("Warning: Packet with key %s coulnd't be added to the packet list!", packetKey);
				continue;
			}
			//CConsole::outDebOnly("Replacing contents for packet with type %.2X, key: %s and identifier %.16X", copiedPacket->getType(), packetKey, copiedPacket->getPacketIdentifier());

			//find the packet in the list
			CBasicPacket* targetPacket = this->packetList->find(CStringKeyCS(packetKey));

			//copy packet content
			memcpy(targetPacket, copiedPacket, sizeof(CBasicPacket));

			//delete the copied packet (not needed anymore)
			delete copiedPacket;

			//break the loop
			added = true;
		}
	}
	this->packetList->unlock();
	return 0;
}


unsigned short CChar::getExpForNextLevel() {
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[8192];
	int count;
	unsigned short nextExp = 0;

	memset(query, 0, 2000);
	sprintf(query, "SELECT exp FROM exp_table WHERE level = %i",this->m_lvlMain+1);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res)
		return 0;

	if(row = mysql_fetch_row(res)) 
	{
		 nextExp = atoi(row[0]);
	}

	mysql_free_result(res);

	return nextExp;

}
// apply caps to a given EXP amount based on playerlevel
int CChar::getCappedExp(int baseExp) {
	
	if(this->m_lvlMain <=50) {
		if(baseExp > 200) {
			baseExp = 200;
		}
	} else if(this->m_lvlMain <= 60) {
		if(baseExp > 250) {
			baseExp = 250;
		}
	} else if( baseExp > 300) {
		baseExp = 300;
	}
	return baseExp;
}

bool CChar::isExpChain() {

	if( (this->m_expChain.chainNum == 0) || ((time(NULL) - this->m_expChain.chainStamp) < this->getExpChainTimer())) {
		
		CConsole::outDebOnly("EXP CHAIN %i",this->m_expChain.chainNum);
		return true;

	} else {
		this->m_expChain.chainNum = 0;
		this->m_expChain.chainStamp = 0;
	}

	return false;


}


float CChar::getExpChainMod() {
	switch(this->m_expChain.chainNum) {
		case 0: return 1.0f;
		case 1: return 1.2f;
		case 2: return 1.25f;
		case 3: return 1.3f;
		case 4: return 1.4f;
		default: return 1.5f;
	}
}


// distributes the exp from the mob to all chars in party
void CChar::distributeExp(int baseExp, CBaseEntity * source) {

	float expMod = 1.0f;
	float playerShare = 1.0f;
	int exp = 0;
	//if player is in party, figure out how to distribute the exp
	if(this->m_pParty != NULL) {
	
		std::vector<CChar*> tmpList;

		// get a vectorlist of all partymembers in the same zone
		tmpList = this->getPartyMembersInZone();

		// get a pointer to the highest level char in party
		CChar * pHighChar = this->getHighestLevelCharInParty();

		int partyLevel = pHighChar->m_lvlMain;


		switch(tmpList.size()) {

			case 1:
				playerShare = 1.0f;
				break;
			case 2:
				playerShare = .6f;
				break;
			case 3:
				playerShare = .45f;
				break;
			case 4:
				playerShare = .40f;
				break;
			case 5:
				playerShare = .37f;
				break;
			case 6:
				playerShare = .35f;
				break;
			default:
				playerShare = 1.0f;
				break;

		}
		

		for(unsigned int i = 0; i < tmpList.size(); i++) {

			if(CUtil::distance(this->loc.p, tmpList.at(i)->loc.p) < 50) {

				if((tmpList.at(i)->m_lvlMain  > (partyLevel +7)) || partyLevel > 50) {
					expMod = (float)(tmpList.at(i)->m_lvlMain) / (float)partyLevel;
				} else {
					expMod = (float)(tmpList.at(i)->getExpForNextLevel()) / (float)(pHighChar->getExpForNextLevel());
				}

				// apply party mods to exp
				exp = (int)((baseExp*playerShare)*expMod);

				// apply caps to exp
				exp = tmpList.at(i)->getCappedExp(exp);

				// apply global rate modifier
				exp = (int)((float)exp * CGlobal::expRate);


				if((baseExp >= 100) && tmpList.at(i)->isExpChain()) {

					tmpList.at(i)->m_expChain.chainStamp = (int)time(NULL);
					exp = (int)((float)exp * tmpList.at(i)->getExpChainMod());
					if(tmpList.at(i)->m_expChain.chainNum != 0) {
						tmpList.at(i)->quePacket(CMessagePacketTwo(tmpList.at(i), source, exp, tmpList.at(i)->m_expChain.chainNum, 253));
					} else {
						tmpList.at(i)->quePacket(CMessagePacketTwo(tmpList.at(i), source, exp, 0, 0x08));
					}
					tmpList.at(i)->m_expChain.chainNum++;

				} else {

					// show the exp gained message
					tmpList.at(i)->quePacket(CMessagePacketTwo(tmpList.at(i), source, exp, 0, 0x08));

				}

				// actually gain the exp
				tmpList.at(i)->gainExp(exp);

			} else {
				// display out of range message
				tmpList.at(i)->quePacket(CMessagePacket(tmpList.at(i), tmpList.at(i), 0x00, 0x00, 37));
			}

		}

		// player is alone
	} else {

		// apply caps to baseExp
		exp = this->getCappedExp(baseExp);

		// apply global rate modifier
		exp = (int)((float)exp * CGlobal::expRate);

		if((baseExp >= 100) && this->isExpChain()) {

			this->m_expChain.chainStamp = (unsigned int)time(NULL);
			exp = (int)((float)exp * this->getExpChainMod());
			if(this->m_expChain.chainNum != 0) {
				this->quePacket(CMessagePacketTwo(this, source, exp, this->m_expChain.chainNum, 253));
			} else {
				this->quePacket(CMessagePacketTwo(this, source, exp, 0, 0x08));
			}
			this->m_expChain.chainNum++;

		} else {

			// show the exp gained message
			this->quePacket(CMessagePacketTwo(this, source, exp, 0, 0x08));

		}

		// actually gain the exp
		this->gainExp(exp);
	}

}

//adds exp to the char
void CChar::gainExp(int baseExp) {


	if((this->m_lvlMain == this->m_levelCap) && ((this->m_expCurrent + baseExp) >= this->m_expForNextLevel)) {
		this->m_expCurrent = this->m_expForNextLevel - 1;
	} else {
		this->m_expCurrent += baseExp;
	}
	if((this->m_expCurrent-this->m_expForNextLevel) >= 0) {
		//levelup
		if(this->m_lvlMain != this->m_levelCap) {
			this->gainLevel();
		}
		this->writeChar();
	}
	this->quePacket(CCharStatsPacket(this));
}


int CChar::getExpChainTimer() {

	if(this->m_lvlMain <= 10) {
		switch(this->m_expChain.chainNum) {
			case 0: return 50;
			case 1:	return 40;
			case 2: return 30;
			case 3: return 20;
			case 4: return 10;
			case 5: return 1;
			default: return 0;
		}

	} else if(this->m_lvlMain <= 20) {
		switch(this->m_expChain.chainNum) {
			case 0: return 100;
			case 1:	return 80;
			case 2: return 60;
			case 3: return 40;
			case 4: return 20;
			case 5: return 1;
			default: return 0;
		}
	
	} else if(this->m_lvlMain <= 30) {
		switch(this->m_expChain.chainNum) {
			case 0: return 150;
			case 1:	return 120;
			case 2: return 90;
			case 3: return 60;
			case 4: return 30;
			case 5: return 1;
			default: return 0;
		}

	} else if(this->m_lvlMain <= 40) {
		switch(this->m_expChain.chainNum) {
			case 0: return 200;
			case 1:	return 160;
			case 2: return 120;
			case 3: return 80;
			case 4: return 40;
			case 5: return 1;
			default: return 0;
		}

	} else if(this->m_lvlMain <= 50) {
		switch(this->m_expChain.chainNum) {
			case 0: return 250;
			case 1:	return 200;
			case 2: return 150;
			case 3: return 100;
			case 4: return 50;
			case 5: return 1;
			default: return 0;
		}
	} else if(this->m_lvlMain <= 60) {
		switch(this->m_expChain.chainNum) {
			case 0: return 300;
			case 1:	return 240;
			case 2: return 180;
			case 3: return 120;
			case 4: return 90;
			case 5: return 60;
			default: return 60;
		}

	} else {
		switch(this->m_expChain.chainNum) {
			case 0: return 360;
			case 1:	return 300;
			case 2: return 240;
			case 3: return 165;
			case 4: return 105;
			case 5: return 60;
			default: return 60;
		}

	}
	return 0;

}
//Level-up
void CChar::gainLevel() {

	this->m_lvlMain += 1;
	if(this->m_hasRaise == 0) {
		this->insertPacket(CHAR_INRANGE_SELF, CMessagePacketTwo(this, NULL, this->m_lvlMain, 0, 0x09));
	}
	CStats::calcStats(this);
	this->m_expCurrent = this->m_expCurrent-this->m_expForNextLevel;
	this->m_expForNextLevel = this->getExpForNextLevel();
	if(this->m_expCurrent >= this->m_expForNextLevel) {
		this->m_expCurrent = this->m_expForNextLevel-1;
	}
	this->h.hp = this->getMaxHp();
	this->h.mp = this->getMaxMp();

	this->updateAbilities();
	this->updateHealth();
	this->quePacket(CCharStatsPacket(this));
	this->quePacket(CCharSkillPacket(this));
	this->sendLevel();
	this->updateChar();
	
}

unsigned short CChar::getSkill(unsigned short id){
	return this->s.skillId[id];
}

unsigned char CChar::getSkillRank(unsigned short id) {
	return this->s.rank[id];
}

void CChar::setSkill(unsigned short id, unsigned short value){
	this->s.skillId[id] = value;
}

void CChar::incSkill(unsigned short id, unsigned short value){
	this->s.skillId[id] += value;
}

// disengage a target
void CChar::disengage() {
	// animation = disengage
	this->setAnimation(0x00);
	// update the character animation to the char (other players will be updated with the 0x0d packet)
	this->updateChar();
}

void CChar::lockOn(CBaseEntity * target) {
	this->quePacket(CLockOnPacket(this, target));
}

// engage a target
void CChar::engage(CBaseEntity * target) {
	// animation = engage
	this->setAnimation(0x01);
	// update the character animation to the char (other players will be updated with the 0x0d packet)
	// Locks the charakter on to his target
	this->lockOn(target);
	this->updateChar();
	
}

//returns skill id for weapons
unsigned char CChar::getWeaponType(unsigned char slotID) {
	CWeaponItem * weapon=NULL;
	weapon = getWeaponItem(slotID);
	if(weapon != NULL)
		return weapon->getSkillType();
	return WEAPON_NONE;
}

unsigned short CChar::getDelay(unsigned char slotID) {
	CWeaponItem * weapon=NULL;
	weapon = getWeaponItem(slotID);
	if(weapon != NULL) {
		if(weapon->getId() == 0x40EE)
			return 100;
		if(weapon->getSkillType() == WEAPON_H2H)
			return (weapon->getDelay() + 480);  // Delays on H2H weapons are increases on base.  Base speed is 480
		return (weapon->getDelay() + 240);
	}
	return 480;  // Default delay without a weapon in main hand
}

unsigned short CChar::getWeaponDamage(unsigned char slotID) {
	CWeaponItem * weapon=NULL;
	weapon = getWeaponItem(slotID);
	if(weapon != NULL) {
		if(weapon->getId() == 0x40EE)
			return 4000;
		return weapon->getDamage();
	}
	return 0;
}

//get the max skill of any skill based on character level
unsigned short CChar::getMaxSkill(unsigned short id) 
{
	return CDb::getObj()->getMaxWeaponSkill(id, this->m_jobMain, this->m_lvlMain);
}

void CChar::doSkillUp(unsigned char skillID, unsigned short checkSkill) {
	unsigned short currentSkill = this->getSkill(skillID);
	unsigned short maxSkill = 0;
	if(skillID<=47)
		maxSkill = this->getMaxSkill(skillID);
	else if( (skillID>=48) && (skillID<=56) )
		maxSkill = ((s.rank[skillID]+1)*10);
	CConsole::outDebOnly("SkillID: %i  CurrentSkill: %i  MaxSkill: %i", skillID, (currentSkill/10), maxSkill);

	short skillDiff = checkSkill - (currentSkill/10);
	CConsole::outDebOnly("CheckSkill: %i  Difference: %i", checkSkill, skillDiff);

	double chance = 0;
	if(skillDiff > 9)
		chance = 0.95;
	else if( (skillDiff > 0) && (skillDiff <= 9) )
		chance = 0.05 + ((float)skillDiff / 10);

	double random = 0;
	random = rand() / ((double) RAND_MAX);
	CConsole::outDebOnly("1st Chance: %g  Random: %g",chance,random);

	if(maxSkill > 0) {
		if( (currentSkill/10) < maxSkill) {
			if( random < chance) {
				int skillAmount = 1;

				int tier = 0;
				// Every 10 levels = 1 tier
				tier = (currentSkill/100);
				CConsole::outDebOnly("Tier for 2nd - 5th chances: %i",tier);
				
				chance = 1.00 / pow((double)2, (tier+1));
				random = rand() / ((double) RAND_MAX);
				CConsole::outDebOnly("2nd Chance: %g  Random: %g",chance,random);
				if(random < chance) skillAmount+=1;
				
				chance = chance / 2;
				random = rand() / ((double) RAND_MAX);
				CConsole::outDebOnly("3rd Chance: %g  Random: %g",chance,random);
				if(random < chance) skillAmount+=1;
				
				chance = chance / 2;
				random = rand() / ((double) RAND_MAX);
				CConsole::outDebOnly("4th Chance: %g  Random: %g",chance,random);
				if(random < chance) skillAmount+=1;
				
				chance = chance / 2;
				random = rand() / ((double) RAND_MAX);
				CConsole::outDebOnly("5th Chance: %g  Random: %g",chance,random);
				if(random < chance) skillAmount+=1;

				if( (skillAmount + currentSkill) > maxSkill*10 )
					skillAmount = maxSkill*10 - currentSkill;

				this->incSkill(skillID, skillAmount);
				this->quePacket(CMessagePacket(this, this, skillID, skillAmount, 0x26)); 

				if( (currentSkill/10) < (currentSkill + skillAmount)/10 ) {
					this->quePacket(CMessagePacket(this, this, skillID, (currentSkill + skillAmount)/10, 0x35));
					if(skillID<=26) {
						MYSQL_RES * res;
						MYSQL_ROW row;
						char query[1024];

						sprintf(query, "SELECT ID FROM weaponskills WHERE SkillID = %i AND MinSkillLvl = %i",skillID,(currentSkill+skillAmount)/10);
						//CConsole::outDeb("%s\n",query);  //DEBUG
						int count = (int) strlen(query);

						MYSQL* mysqlConn=CDb::getConn(); //get conn
						mysql_real_query(mysqlConn, query, count);

						res = mysql_store_result(mysqlConn);
						CDb::pushConn(mysqlConn); //release conn

						if (res)
						{
							if(row = mysql_fetch_row(res)) {
								unsigned int wsid;
								wsid = atoi(row[0]);
								this->learnWeaponSkill(wsid);
							}
							mysql_free_result(res);
						}
					}
				}
			}
		}
	}
}

void CChar::learnWeaponSkill(unsigned short id) {
	this->quePacket(CMessagePacket(this, this, id, 0x00, 0x2D));
	this->updateAbilities();
}

unsigned int CChar::getCritHitRateOn(CBaseEntity * pTarget) {

	int crithitrate = 5;
	int playerdex = this->stats.dex + this->stats.pDex;
	int monsteragi = pTarget->stats.agi + pTarget->stats.pAgi;
	int dDEX =  playerdex - monsteragi;
	
	if (dDEX < 0){
		dDEX = 0;
	}
	if (dDEX > 50){
		dDEX = 50;
	}
	//dDEX is between 0 and 50.
	int crithitbonus = (int)(dDEX * 0.3f);

	//add on the bonus to the base....
	crithitrate += crithitbonus;
	return crithitrate;

}

void CChar::attackAction( CBaseEntity* target) {

	
	int damage = 0;
	int numattacks = 1;
	int realActions = 1;
	bool critical = false;
	int totaldmg = 0;
	unsigned int crithitrate = 0;
	if(target == NULL) {
		return;
	}
	// seed the random
	srand(clock());

	crithitrate = this->getCritHitRateOn(target);

	unsigned short basedmg = this->getWeaponDamage(SLOT_MAIN);

	CActionPacket* attackPacket=new CActionPacket();

	attackPacket->setActorId(this->getId());
	attackPacket->setCategorie(1);
	attackPacket->setStartAnimationCategorie(0x1D);
	attackPacket->setStartAnimation(0x306B);
	attackPacket->setParameter1(0x0061);
	attackPacket->setUnknown(0x00000000);

	numattacks = this->getNumAttacksFrom(SLOT_MAIN);

	for(int i = 0; i < numattacks; i++) {
		apActionEntry newEntry;
		memset(&newEntry,0,sizeof(newEntry));
		
		float ratio = CUtil::randFloat(this->getMinCRatio(target), this->getMaxCRatio(target));
		if(rand()%100 < this->getHitRate(target,SLOT_MAIN)) {

			if((unsigned int)(rand()%100) <= crithitrate) {
				critical = true;
				ratio += 1;

				if (ratio > 3){
					ratio = 3;
				}
			} else {
				critical = false;
			}

			damage = (int)((basedmg + this->getFStr(target))*ratio);

			//Now adjust for weapon types
			damage = target->adjustDamage(this->getWeaponItem(SLOT_MAIN),damage);

			totaldmg += damage;

			newEntry.unknownValue=0x08;
			newEntry.targetAnimationID=0x0100;
			newEntry.unknownValue2=0x020;

			newEntry.messageType=0x0001;
			
			if(damage > 0) {
				this->gainTPFrom(SLOT_MAIN,target);
			}

			if(critical) {
				newEntry.unknownValue2=0x044;
				newEntry.messageType=0x0043;
			}

			newEntry.parameter2=damage;

			unsigned char skillID = 0;
			skillID = this->getWeaponType(SLOT_MAIN);
			this->doSkillUp(skillID, this->getMaxSkill(skillID));

		} else {

			newEntry.unknownValue=0x09;
			newEntry.messageType=0x000f;

		}

		attackPacket->setTargetID(i+1,target->m_id);
		attackPacket->setActionEntry(i+1,1,&newEntry);
		if(totaldmg >= target->getHp()) {
			break;
		}
		realActions++;
		
	}
	attackPacket->finalizePacket();

	this->insertPacket(CHAR_INRANGE_SELF,*attackPacket);
	target->takePhysicalDamage(totaldmg, this);
	if(target->getObjectType() == OBJ_MOB && target->isDead()) {
		((CNpc*)target)->m_timeOfKill = (unsigned int)time(NULL) + realActions;
	}
	
}



int	CChar::setCharVar(const char * variable, int value) {
	CChar * pChar   = this;
	MYSQL_RES * res;
	char query[4384];
	int count = 0;

	memset(query,0,4384);

	//If you are setting the variable to 0 you might as well delete it
	if (value == 0) {
		sprintf(query, "DELETE FROM char_vars WHERE charid = %i AND ident = '%s' ", pChar->m_id, variable);
		count = (int) strlen(query);
		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn
		//Jump out; we aren't doing anything more than deleting
		return 0;
	}

	sprintf(query, "SELECT value FROM char_vars WHERE charid = %i AND ident = '%s' ", pChar->m_id, variable);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		mysql_free_result(res);
		return 0;
	}
	if(!mysql_num_rows(res)){
		memset(query,0,4384);
		sprintf(query, "INSERT INTO char_vars (charid, ident, value) VALUES (%i,'%s',%i) ", pChar->m_id, variable, value);
		count = (int) strlen(query);

		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn

		mysql_free_result(res);
		return 1;
	}
	
	memset(query,0,4384);
	sprintf(query, "UPDATE char_vars SET value = %i WHERE charid = %i AND ident = '%s' ", value,  pChar->m_id, variable);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	mysql_free_result(res);
	
	return 1;
}

// retrieves a variable from a user, returns 0 if not existant
int	CChar::getCharVar(const char * variable) {
	CChar * pChar   = this; 
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4384];
	int count = 0;
    int value;

	memset(query,0,4384);
	sprintf(query, "SELECT value FROM char_vars WHERE charid = %i AND ident = '%s' ", pChar->m_id, variable);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		mysql_free_result(res);
		value = 0;
		return value;
	} 

	if(!mysql_num_rows(res)) {
		mysql_free_result(res);
		value = 0;
		return value;
	}

	row = mysql_fetch_row(res); 
	value = atoi(row[0]);
	mysql_free_result(res);
	
	return value;
	
}

void CChar::takePhysicalDamage(int damage, CBaseEntity * pDamageDealer) {

	// this would need checking for all sorts of additional effects
	this->h.hp -= damage;
	// stop healing if taking dmg
	if((damage > 0) && this->isHealing() ) {
		this->setAnimation(0x00);
		this->isHealing(false);
		this->updateChar();
	}
	//decay CEnmity if this is a mob
	if (pDamageDealer->isMob()) {
		int decay = ((1800 * damage) / this->getMaxHp());
		((CNpc*)pDamageDealer)->updateVCEnmity((CBaseEntity*)this,0,-decay);
	}
	if(this->h.hp <= 0) {
		this->h.hp = 0;
		this->m_isCasting = false;
		this->die();
	} 
	this->updateHealth();

}

void CChar::die() {
	this->setAnimation(0x03);
	//Clear that enmity!  Ok this doesn't seem like it will be easy...
		
	//Lose exp
	int lostExp = 0;
	if (this->m_lvlMain >= 4) {
		lostExp = (int)((double)this->m_expForNextLevel*0.1);
	}
	if (this->m_lvlMain >= 25) {
		lostExp = (int)((double)this->m_expForNextLevel*0.08);
	}
	if (this->m_lvlMain >= 68) {
		lostExp = 2400;
	}
	this->loseExp(lostExp);
	this->updateChar();
	this->clearHasEnmityList();
	// Despawn any pets
	CreateThread(0,0,despawnPet,this,0,0);
}


int CChar::getFStr(CBaseEntity * target) {
	int dif = (this->stats.str + this->stats.pStr) - (target->stats.vit + target->stats.pVit);
	int rank = this->getWeaponDamage(SLOT_MAIN) /9; 

	int fstr = 0; 

	if (dif >= 12) {
		fstr = (dif+4)/4;
	} else if(dif >= 1 && dif <= 5) {
		fstr = (dif+6)/4;
	} else if(dif >= 6 && dif <= 11) {
		fstr = (dif+7)/4;
	} else if(dif >= -2 && dif <= 0) {
		fstr = (dif+8)/4;
	} else if(dif >= -7 && dif <= -3) {
		fstr = (dif+9)/4;
	} else if(dif >= -15 && dif <= -8) {
		fstr = (dif+10)/4;
	} else if(dif >= -21 && dif <= -16) {
		fstr = (dif+12)/4;
	} else if(dif <= -22) {
		fstr = (dif+13)/4;
	}

	if(fstr < (rank * (-1))) {
		return (rank * (-1));
	}
	if((fstr > (rank * (-1))) && (fstr <= rank +8)) {
		return fstr;
	} else {
		return rank +8;
	}

}

int CChar::getFStrTwo(CBaseEntity * target) {
	int dif = (this->stats.str + this->stats.pStr) - (target->stats.vit + target->stats.pVit);
	int rank = 0;
	if (this->getWeaponItem(SLOT_RANGED) != 0) {
		rank = 2 * this->getWeaponDamage(SLOT_RANGED) /9; 
	} else {
		rank = 2 * this->getWeaponDamage(SLOT_AMMO) /9; 
	}
	int fstr = 0; 

	if (dif >= 12) {
		fstr = (dif+4)/2;
	} else if(dif >= 1 && dif <= 5) {
		fstr = (dif+6)/2;
	} else if(dif >= 6 && dif <= 11) {
		fstr = (dif+7)/2;
	} else if(dif >= -2 && dif <= 0) {
		fstr = (dif+8)/2;
	} else if(dif >= -7 && dif <= -3) {
		fstr = (dif+9)/2;
	} else if(dif >= -15 && dif <= -8) {
		fstr = (dif+10)/2;
	} else if(dif >= -21 && dif <= -16) {
		fstr = (dif+12)/2;
	} else if(dif <= -22) {
		fstr = (dif+13)/2;
	}

	if(fstr < (rank * (-1))) {
		return (rank * (-1));
	}
	if((fstr > (rank * (-1))) && (fstr <= rank +8)) {
		return fstr;
	} else {
		return rank +8;
	}

}

DWORD WINAPI CharBattleServ(LPVOID lpParam) {

	CChar * schar = (CChar*)lpParam;

	CBaseEntity * target = schar->m_pTarget;

	if (target == NULL)
		return 0;

	if (target == schar) {
		CConsole::outDebOnly("Prevented char '%s' from fighting self",schar->getName());
		schar->disengage();
		return 0;
	}
	unsigned short weaponDelay;
	

	schar->engage(target);

	bool hit = FALSE;

	schar->oldAttackTime = clock();

	while((schar->getAnimation() != 0x00) && (schar->getAnimation() != 0x03)) {
		weaponDelay = schar->getDelay(SLOT_MAIN);
		if (schar->loggedOut) {
			target->incombat = false;
			break;
		}

	
		if((((long long)clock() - schar->oldAttackTime) > ((float)(weaponDelay/60)*1000)) && !schar->isCasting()) {


			if(target->getObjectType() == OBJ_MOB) {
				CChar * tmpChar = CChar::getCChar(((CNpc*)target)->ownerId, "", 0);

				if((((CNpc*)target)->ownerId == schar->getId()) || (((CNpc*)target)->ownerId == 0) || (tmpChar != NULL && schar->isPartyMember(tmpChar))) {

				} else {
					schar->disengage();
					return 0;
				}
				if(!schar->isFaceing(target->loc.p)) {
					schar->quePacket(CMessagePacket(schar, target, 0x00, 0x00, 0x05));
					schar->oldAttackTime = (long long) clock();
					continue;
				}
				if( (CUtil::distance(schar->loc.p,target->loc.p) > (4+((CNpc*)target)->sizeMod))) {
					schar->quePacket(CMessagePacket(schar, target, 0x00, 0x00, 0x04));
					schar->oldAttackTime = (long long) clock();
					continue;
				}
			} else {
				if((CUtil::distance(schar->loc.p,target->loc.p) > 3)) {
					schar->quePacket(CMessagePacket(schar, target, 0x00, 0x00, 0x04));
					schar->oldAttackTime = (long long) clock();
					continue;
				}
			}

			schar->attackAction(target);

			if(target->m_objType == OBJ_PC) {
				((CChar*)(target))->updateChar();
			}

			schar->oldAttackTime = (long long) clock();
			
		}

		if(schar->isCasting() || schar->isRangedAttacking()) {
			schar->oldAttackTime = (long long) clock();
		}

		if(target->isDead() || ((CNpc*)target)->isPet()) {
			if((target->getObjectType() == OBJ_MOB && ((CNpc*)target)->m_pLastDD == schar)) {
				if((((CNpc*)target)->m_timeOfKill != 0)) {
					Sleep( (((CNpc*)target)->m_timeOfKill - time(NULL))*1100); 
				}
				schar->disengage();
			} else {
				schar->disengage();
			}
			target = NULL;
			return 0;
		}
		Sleep(10);
	}
	return 0;
}

void CChar::setBoundary(unsigned short boundaryNum) {
	CGlobal::waitCharList();
	this->boundaryId = boundaryNum;
	CGlobal::exitCharList();
}

int CChar::sendPartyInvite(CChar * invitee) {

	//Make sure the player doesn't invite themself... they may need to find some friends otherwise ;)
	if (this->getId() != invitee->getId()) {
		//Verify the person we are inviting is not in a party and does not have a party invite
		if ((invitee->inParty == 0) && (invitee->invitePending == 0)) {
			//Check if the inviter is in a party
			if (this->inParty == 0) {
				//Not in a party, don't need to check for leader status
				invitee->invitePending = this->getId();
				invitee->quePacket(CPartyInvitePacket(this,invitee,INVITE_PARTY));
			//Verify that the inviter is actually in a party and is not a null pointer
			} else if (this->getParty() != NULL) {
				//Verify that the inviters party has a leader and is not a null pointer
				if (this->getParty()->getLeader() != NULL) {
					//Check to make sure the leader of the party is the inviter and we have room
					if ( (this->getParty()->getLeader()->getId() == this->getId()) && (this->getParty()->memberCount() < 6)) {
						invitee->invitePending = this->getId();
						invitee->quePacket(CPartyInvitePacket(this,invitee,INVITE_PARTY));
						if (invitee->getTreasurePool()->hasTreasure()) {
							invitee->quePacket(CStandardMessagePacket(PARTY_ITEMS_LOST));
						}
					}
				}
			}
		
			//If we are inviting someone already in a party; it could be for an alliance.
		} else if ( (invitee->inParty > 0) && (invitee->invitePending == 0) && (invitee->getParty() != NULL) ) {
			//Verify that the inviter is in a party
			if ( (this->inParty > 0) && (this->getParty() != NULL) ) {
				//Now that we know both players are in parties, verify that they are their respective party leaders
				//	Also, verify that the invitee is not in an alliance already
				if ( (this->getParty()->getLeader()->getId() == this->getId()) && (invitee->getParty()->getLeader()->getId() == invitee->getId()) && (invitee->getParty()->inAlliance == 0) ) {
					//Check to see if the inviter is in an alliance
					if ( (this->getParty()->inAlliance > 0) && (this->getParty()->getAlliance() != NULL) ) {
						//The inviter is in an alliance, verify they are the alliance leader and we have room for them
						if ( (this->getParty()->getAlliance()->getMainParty()->getLeader()->getId() == this->getId()) && (this->getParty()->getAlliance()->partyCount() < 3) ) {
							//Everything checks out, invite them to the alliance.
							invitee->invitePending = this->getId();
							invitee->quePacket(CPartyInvitePacket(this,invitee,INVITE_ALLIANCE));
						}
					} else {
						//A Party leader is inviting a party leader, could be the start of an alliance.
						invitee->invitePending = this->getId();
						invitee->quePacket(CPartyInvitePacket(this,invitee,INVITE_ALLIANCE));
					}
				}
			}
		} else {
			//Cannot send an invite at this time
		}
	}
	return 0;
}

//Forces the player to leave a party/Alliance if they are in one (used for disconnects)
int CChar::forcePartyLeave(void) {
	//Verify that the player is in a party
	if ( (this->inParty > 0) && (this->getParty() != NULL) ) {
		//Check to see if they are a party leader, quarter master, or lvlsync target
		if ((this->getParty()->getLeader() == this) || (this->getParty()->getQM() == this) || (this->getParty()->getLvlSync() == this)) {
			//If the party has more than 1 member, toss leader to the next person
			if (this->getParty()->memberCount() > 1) {
				//Check every member inside of the party
				this->getParty()->lockPartyList();
				for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
					CChar * partyChar = this->getParty()->members.at(y);
					//If the next party member is not this person and is not a NULL pointer
					if ((partyChar != NULL) && (partyChar != this)) {
						//Shift the party role to the next member
						if (this->getParty()->getLeader() == this)
							this->getParty()->setLeader(partyChar);
						if (this->getParty()->getQM() == this)
							this->getParty()->setQM(NULL);
						if (this->getParty()->getLvlSync() == this)
							this->getParty()->setLvlSync(NULL);

						//We can safely unlock the party list since we will break out
						this->getParty()->unlockPartyList();
						break;
					}
				}
				//Safety release check
				this->getParty()->unlockPartyList();

				//Party only has one member, check and disban from any alliance.
			} else if ((this->getParty()->inAlliance > 0) && (this->getParty()->getAlliance() != NULL)) {
				//If the player is not the leader of the alliance leave the alliance
				if (this->getParty() != this->getParty()->getAlliance()->getMainParty()) {
					this->getParty()->getAlliance()->removeParty(this->getParty());
				} else {
					//if the player is the leader of the alliance disband the alliance
					this->getParty()->getAlliance()->dissolveAlliance();
				}
				this->setParty(NULL);
				if (this->hasTreasurePool()) {
					this->getTreasurePool()->removeMember(this);
				}
			} else {
				//Last one in a single person party
				this->getParty()->disbandParty();
			}
		}
		//Now we may leave the party
		this->getParty()->removeMember(this);
	}

	return 0;
}

int CChar::partyInviteHandler(unsigned int result) {
	//Check to see if the party invite was accepted
	if (result == 1) {
		//Verify that the inviter still exists
		CChar * inviter = CChar::getCChar(this->invitePending,"",0);
		if (inviter != NULL) {
			//Check to see if the inviter is in a party
			if (inviter->inParty == 0) {
				//Not in a party create a new party
				inviter->setParty(new CParty());
				//assign the party leader to the inviter
				inviter->getParty()->setLeader(inviter);
				//add the inviter to the party
				inviter->getParty()->addMember(inviter);
				//If seek flag is on turn it off
				if (inviter->h.flags & FLAG_INVITE) {
					inviter->h.flags ^= FLAG_INVITE;
					inviter->updateChar();
				}
				//add yourself to the party
				inviter->getParty()->addMember(this);
				//If seek flag is on turn it off
				if (this->h.flags & FLAG_INVITE) {
					this->h.flags ^= FLAG_INVITE;
					this->updateChar();
				}

				//Send a packet to everyone in the party
				//	There is NO possibility of being in an alliance here, so INPARTY works fine
				this->insertPacket(CHAR_INPARTY_SELF,CDefinePartyPacket(inviter->getParty()));
			} else {
				//Verify that the party we are joining still is a party
				if (inviter->getParty() != NULL) {
					//Make sure we still have a party leader
					if (inviter->getParty()->getLeader() != NULL) {
						//Make sure the inviter is the party leader
						if (inviter->getParty()->getLeader() == inviter) {
							//Check to see if the person accepting has a party (possibility of joining an alliance)
							if (this->getParty() != NULL) {
								//Verify That the person accepting the invite is the party leader
								if (this->getParty()->getLeader() == this) {
									//Joining the alliance

									//Do we have an alliance to join already?
									if (inviter->getParty()->inAlliance > 0) {
										//Verify that there is space in the alliance
										if (inviter->getParty()->getAlliance()->partyCount() < 3) {
											//We have space; lets join
											inviter->getParty()->getAlliance()->addParty(this->getParty());
											this->insertPacket(CHAR_INALLIANCE_SELF,CDefinePartyPacket(inviter->getParty()));
										}
									} else {
										//Create a new alliance for the parties							
										//Not in a party create a new party
										inviter->getParty()->setAlliance(new CAlliance());
										//assign the party leader to the inviter
										inviter->getParty()->getAlliance()->setMainParty(inviter->getParty());
										//add the inviter to the party
										inviter->getParty()->getAlliance()->addParty(inviter->getParty());
										//add yourself to the party
										inviter->getParty()->getAlliance()->addParty(this->getParty());
										//Send a packet to everyone in the party
										this->insertPacket(CHAR_INALLIANCE_SELF,CDefinePartyPacket(inviter->getParty()));
									}
								}
							} else {
								//Not an alliance, just join the party instead.

								//Ensure that there is a spot in the party left
								if (inviter->getParty()->memberCount() < 6) {
									//add yourself to the party
									inviter->getParty()->addMember(this);
									//Send a packet to everyone in the alliance / party
									this->insertPacket(CHAR_INALLIANCE_SELF,CDefinePartyPacket(inviter->getParty()));
									//If seek flag is on turn it off
									if (this->h.flags & FLAG_INVITE) {
										this->h.flags ^= FLAG_INVITE;
										this->updateChar();
									}
								}
							}
						}
					}
				}
			}
		}
	} else if (result == 0) { //Party was declined
		//Send the your invitation was declined message
		CChar * inviter = CChar::getCChar(this->invitePending,"",0);
		if (inviter != NULL) {
			inviter->quePacket(CStandardMessagePacket(PARTY_DECLINED));
		}
	}
	//Regardless of the result, we have delt with the pending invite; we can reset it.
	this->invitePending = 0;

	return 0;
}


int CChar::updatePartyStats(void) {
	//Verify the player has a party
	if ( (this->getParty() != NULL) && (this->inParty) ) {
		if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
			this->getParty()->getAlliance()->lockAllianceList();
			//The party is part of an alliance, now check all of the parties in the alliance for members
			for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
				//Get the first party from the alliance
				CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
				//Verify the pointer of the party is not null.
				if (subParty != NULL) {
					subParty->lockPartyList();
					//Check every member inside of the alliance party
					for (unsigned int y = 0; y < subParty->members.size(); y++) {
						CChar * partyChar = subParty->members.at(y);
						if (partyChar != NULL) {
							partyChar->quePacket(CUpdatePartyPacket(partyChar,this,this->getParty()->inAlliance,this->inParty));
						}
					}
					subParty->unlockPartyList();
				}
			}
			this->getParty()->getAlliance()->unlockAllianceList();
			//Not in an alliance
		} else {
			//Check every member inside of the party
			this->getParty()->lockPartyList();
			for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
				CChar * partyChar = this->getParty()->members.at(y);
				if (partyChar != NULL) {
					partyChar->quePacket(CUpdatePartyPacket(partyChar,this,0,this->inParty));
				}
			}
			this->getParty()->unlockPartyList();
		}
	}
	return 0;
}

// returns true if the spell currently has cooldown for the char.
bool CChar::hasSpellCooldown(unsigned int spellID) {
	for(unsigned int i = 0; i < this->recastSpellList.size(); i++) {
		if(this->recastSpellList.at(i).spellID == spellID) {
			return true;
		}
	}
	return false;
}

bool CChar::hasAbilityCooldown(unsigned int recastID) {
	for(unsigned int i = 0; i < this->recastAbilityList.size(); i++) {
		if(this->recastAbilityList.at(i).recastID == recastID) {
			return true;
		}
	}
	return false;
}

CParty * CChar::getParty()
{
	return m_pParty;
}

void CChar::setParty(CParty * party)
{
	//If your not in a party we can set inParty to 0
	if (party == NULL)
		this->inParty = 0;
	this->m_pParty=party;
}

//Checks to see if player is in the same party as the calling person
bool CChar::isPartyMember(CChar * player) 
{
	//If this and player have the same party pointer, and their party pointer is not NULL 
	// then they could be said to be in the same party
	if ( (this->getParty() == player->getParty()) && (this->getParty() != NULL) ) {
		return true;
	} else if ((this->getParty() != NULL) && (player->getParty() != NULL)) {
		if ( (this->getParty()->getAlliance() == player->getParty()->getAlliance()) && (this->getParty()->getAlliance() != NULL)) {
			//same alliance
			return true;
		} else {
			return false;
		}
	} else {
		//They are not in the same party, one of them may not even be in a party!
		return false;
	}
}

//Checks to see if player is in the same party as the calling person
bool CChar::isAllianceMember(CChar * player) 
{
	//Make sure that their party pointer is not NULL 
	// then they may be in the same party or alliance
	if ((this->getParty() != NULL) && (player->getParty() != NULL)) {
		//Lets see if they are in the same alliance
		if ( (this->getParty()->getAlliance() == player->getParty()->getAlliance()) && (this->getParty()->getAlliance() != NULL)) {
			//same alliance
			return true;
		} else {
			return false;
		}
	} else {
		//One of them is not even in a party
		return false;
	}
}

//Returns all Alliance members of the calling player
std::vector<CChar*> CChar::getAllianceMembers() {
	//Defines the returnList
	std::vector<CChar*> returnList;
	//Clears the value to be safe
	returnList.size();
	//Verify that the player has a party
	if (this->getParty() != NULL) {
		//Lock the party list so nothing will modify it while we copy it
		if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
			this->getParty()->getAlliance()->lockAllianceList();
			//The party is part of an alliance, now check all of the parties in the alliance for members
			for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
				//Get the first party from the alliance
				CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
				//Verify the pointer of the party is not null.
				if (subParty != NULL) {
					subParty->lockPartyList();
					//Check every member inside of the alliance party
					for (unsigned int y = 0; y < subParty->members.size(); y++) {
						CChar * partyChar = subParty->members.at(y);
						if (partyChar != NULL) {
							returnList.push_back(partyChar);
						}
					}
					subParty->unlockPartyList();
				}
			}
			this->getParty()->getAlliance()->unlockAllianceList();
		} 
	}
	//Return the list
	return returnList;
}

//Returns all Alliance members of the calling player in the same zone
std::vector<CChar*> CChar::getAllianceMembersInZone() {
	//Defines the returnList
	std::vector<CChar*> returnList;
	//Clears the value to be safe
	returnList.size();
	//Verify that the player has a party
	if (this->getParty() != NULL) {
		//Lock the party list so nothing will modify it while we copy it
		if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
			this->getParty()->getAlliance()->lockAllianceList();
			//The party is part of an alliance, now check all of the parties in the alliance for members
			for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
				//Get the first party from the alliance
				CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
				//Verify the pointer of the party is not null.
				if (subParty != NULL) {
					subParty->lockPartyList();
					//Check every member inside of the alliance party
					for (unsigned int y = 0; y < subParty->members.size(); y++) {
						CChar * partyChar = subParty->members.at(y);
						if (partyChar != NULL) {
							if (partyChar->getZone() == this->getZone()) {
								returnList.push_back(partyChar);
							}
						}
					}
					subParty->unlockPartyList();
				}
			}
			this->getParty()->getAlliance()->unlockAllianceList();
		} 
	}
	//Return the list
	return returnList;
}


//Returns all party members of the calling player
std::vector<CChar*> CChar::getPartyMembers() {
	//Defines the returnList
	std::vector<CChar*> returnList;
	//Clears the value to be safe
	returnList.size();
	//Verify that the player has a party
	if (this->getParty() != NULL) {
		//Lock the party list so nothing will modify it while we copy it
		this->getParty()->lockPartyList();
		//Get the party members
		returnList.assign(this->getParty()->members.begin(),this->getParty()->members.end());
		//We are done, we can unlock it now
		this->getParty()->unlockPartyList();

	}
	//Return the list
	return returnList;
}

//Finds all party members within the given range of the position
std::vector<CChar*> CChar::getPartyMembersInRange(position pos,unsigned int range) {
	//Check to see if the player has a party
	std::vector<CChar*> returnList;
	//clear the return list to be safe
	returnList.clear();
	//Check to see if player is in a party
	if (this->getParty() != NULL) {
		//If the player is in a party get the party members
		std::vector<CChar*> partyList = this->getPartyMembers();
		//Check to see if the player has a partyList
		if (!partyList.empty()) {
			//There is a party list, loop through the party members and check their distance
			for (unsigned int x = 0; x < partyList.size(); x++) {
				//if this party member is not null
				if (partyList.at(x) != NULL) {
					//Check the distance between the position and the party member (and odviously check that they are in the same zone)
					if ( (CUtil::distance(pos,partyList.at(x)->loc.p) <= range) && (this->getZone() == partyList.at(x)->getZone()) ) {
						//The party member is within acceptable range, add to the returning list
						returnList.push_back(partyList.at(x));
					}
				}
			}
		}
	}
	//Return the list of members that are within range
	return returnList;
}

//Finds all party members within the players zone
std::vector<CChar*> CChar::getPartyMembersInZone() {
	//Check to see if the player has a party
	std::vector<CChar*> returnList;
	//clear the return list to be safe
	returnList.clear();
	//Check to see if player is in a party
	if (this->getParty() != NULL) {
		//If the player is in a party get the party members
		std::vector<CChar*> partyList = this->getPartyMembers();
		//Check to see if the player has a partyList
		if (!partyList.empty()) {
			//There is a party list, loop through the party members and check their distance
			for (unsigned int x = 0; x < partyList.size(); x++) {
				//if this party member is not null
				if (partyList.at(x) != NULL) {
					// check that they are in the same zone
					if ((this->getZone() == partyList.at(x)->getZone()) ) {
						//The party member is in zone, add to the returning list
						returnList.push_back(partyList.at(x));
					}
				}
			}
		}
	}
	//Return the list of members that are within range
	return returnList;
}

// returns the current level of the party
CChar * CChar::getHighestLevelCharInParty() {

	int partylevel = 0;
	CChar * tmpchar = NULL;
	//Check to see if player is in a party
	if (this->getParty() != NULL) {
		//If the player is in a party get the party members
		std::vector<CChar*> partyList = this->getPartyMembers();
		//Check to see if the player has a partyList
		if (!partyList.empty()) {
			//There is a party list, loop through the party members and check their distance
			for (unsigned int x = 0; x < partyList.size(); x++) {
				//if this party member is not null
				if (partyList.at(x) != NULL) {
					// check that they are in the same zone
					if ((this->getZone() == partyList.at(x)->getZone()) ) {
						//The party member is in zone, add to the returning list
						if((tmpchar == NULL ) || partyList.at(x)->m_lvlMain > tmpchar->m_lvlMain) {
							tmpchar =  partyList.at(x);
						}
					}
				}
			}
		}
	}
	//Return the list of members that are within range
	return tmpchar;

}

//Updates the party members locations on your map
int CChar::sendPartyMembersMap() {
	//Verify the player has a party
	if (this != NULL) {
		if ( (this->getParty() != NULL) && (this->inParty) ) {
			if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
				this->getParty()->getAlliance()->lockAllianceList();
				//The party is part of an alliance, now check all of the parties in the alliance for members
				for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
					//Get the first party from the alliance
					CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
					//Verify the pointer of the party is not null.
					if (subParty != NULL) {
						subParty->lockPartyList();
						//Generate map position for each member
						for (unsigned int y = 0; y < subParty->members.size(); y++) {
							CChar * partyChar = subParty->members.at(y);
							if (partyChar != NULL) {
								this->quePacket(CPartyMapPacket(partyChar,(unsigned char)this->getZone()));
							}
						}
						subParty->unlockPartyList();
					}
				}
				this->getParty()->getAlliance()->unlockAllianceList();
				//Not in an alliance
			} else {
				//Check every member inside of the party
				this->getParty()->lockPartyList();
				//Generate map position for each member
				for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
					CChar * partyChar = this->getParty()->members.at(y);
					if (partyChar != NULL) {
						this->quePacket(CPartyMapPacket(partyChar,(unsigned char)this->getZone()));
					}
				}
				this->getParty()->unlockPartyList();
			}
		} else {
			//Not in a party, send a self-update to stop the downloading data
			this->quePacket(CPartyMapPacket(this,(unsigned char)this->getZone()));
		}
	}
	return 0;
}

// Will set and return the appropriate treasure pool
CTreasurePool * CChar::getTreasurePool() {
	//Do we have a treasure pool already?
	if (this->m_pTreasurePool != NULL) {
		return this->m_pTreasurePool;
	} else {
		//Generate a new treasure pool
		if ((this->getParty() != NULL) && (this->inParty)) {
			//We have a party; look to see if a treasure pool exists already
			if ( (this->getParty()->inAlliance) && (this->getParty()->getAlliance() != NULL) ) {
				this->getParty()->getAlliance()->lockAllianceList();
				//The party is part of an alliance, now check all of the parties in the alliance for members
				for(unsigned int x = 0; x < this->getParty()->getAlliance()->partyList.size(); x++) {
					//Get the first party from the alliance
					CParty * subParty = this->getParty()->getAlliance()->partyList.at(x);
					//Verify the pointer of the party is not null.
					if (subParty != NULL) {
						subParty->lockPartyList();
						//Check every member inside of the alliance party
						for (unsigned int y = 0; y < subParty->members.size(); y++) {
							CChar * partyChar = subParty->members.at(y);
							//Make sure the member is not NULL
							if (partyChar != NULL) {
								// Check to see if the party member has a treasure pool and they are on the same zone
								if ( (partyChar->hasTreasurePool()) && (partyChar->getZone() == this->getZone()) ) {
									//Set our treasure pool to equal the party members treasure pool
									this->setTreasurePool(partyChar->getTreasurePool());
									this->m_pTreasurePool->addMember(this);
									//Unlock the party list
									subParty->unlockPartyList();
									//unlock the alliance list
									this->getParty()->getAlliance()->unlockAllianceList();
									//Return the treasure pool
									return m_pTreasurePool;
								}
							}
						}
						//Unlock the party list
						subParty->unlockPartyList();
					}
				}
				//Unlock the alliance list
				this->getParty()->getAlliance()->unlockAllianceList();
				//The alliance does not have a treasure pool, make one
				this->setTreasurePool(new CTreasurePool());
				this->m_pTreasurePool->addMember(this);
				return m_pTreasurePool;
			} else {
				//Check every member inside of the party since we are not in an alliance
				this->getParty()->lockPartyList();
				for (unsigned int y = 0; y < this->getParty()->members.size(); y++) {
					CChar * partyChar = this->getParty()->members.at(y);
					//Make sure this member is not a null pointer
					if ((partyChar != NULL) && (partyChar != this)) {
						//Check to see if the party character has a treasure pool and they are in the same zone
						if ( (partyChar->hasTreasurePool()) && (partyChar->getZone() == this->getZone()) ) {
							//Set our treasure pool equal to the party members treasure pool
							this->setTreasurePool(partyChar->getTreasurePool());
							partyChar->m_pTreasurePool->addMember(this);
							//unlock the party
							this->getParty()->unlockPartyList();
							//Return the treasurepool
							return m_pTreasurePool;
						}
					}
				}
				this->getParty()->unlockPartyList();
				//The party does not have a treasure pool, make one
				this->setTreasurePool(new CTreasurePool());
				this->m_pTreasurePool->addMember(this);
				return m_pTreasurePool;
			}
		} else {
			// No party, solo play
			this->setTreasurePool(new CTreasurePool());
			this->m_pTreasurePool->addMember(this);
			return m_pTreasurePool;
		}
	}
}

//Assigns the treasure pool
void CChar::setTreasurePool(CTreasurePool * treasurePool) {
	this->m_pTreasurePool = treasurePool;
}

//Returns if the player has a non-null treasure pool pointer
int CChar::hasTreasurePool() {
	if (this->m_pTreasurePool != NULL) {
		return 1;
	} else {
		return 0;
	}
}

//Drops all items from the treasure pool on the client side
void CChar::clearTreasurePool() {
	for (unsigned int x = 0; x < 10; x++) {
		this->quePacket(CFindItemPacket((unsigned char)x));
	}
}


void CChar::deleteChar(unsigned int charId)
{
	MYSQL* mysqlConn;
	unsigned long  count;
	char query[16384];

	mysqlConn=CDb::getConn(); //get conn

	sprintf(query, "DELETE FROM chars WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_abilities WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_equip WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_exp WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_inventory WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_job WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_look WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_skills WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_stats WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	sprintf(query, "DELETE FROM char_vars WHERE charid = %i",charId);
	count = (int) strlen(query);
	mysql_real_query(mysqlConn, query, count);

	CDb::pushConn(mysqlConn); //release conn
}

void CChar::setUdpPort(u_short newPort)
{
	CGlobal::waitCharList();
	if (newPort!=this->clientConn.udpPort)
	{
		//remove the old port entry (if the udp port changed)
		if (this->clientConn.udpPort!=0)
		{
			//CConsole::outDebOnly("Removing UDP socket %u from character %s", this->clientConn.udpPort, this->m_name);
			CGlobal::unlinkCCharWithUdpPort(this->clientConn.clientIP, this->clientConn.udpPort);
		}

		//set the new port
		this->clientConn.udpPort = newPort;

		//add the new port (if the new port isn't 0)
		if (this->clientConn.udpPort!=0)
		{
			unsigned char first=(this->clientConn.clientIP>>0)&0xFF;
			unsigned char second=(this->clientConn.clientIP>>8)&0xFF;
			unsigned char third=(this->clientConn.clientIP>>16)&0xFF;
			unsigned char fourth=(this->clientConn.clientIP>>24)&0xFF;
			//CConsole::outDebOnly("Assigning character %s from ip %u.%u.%u.%u to UDP socket %i.", this->m_name, first, second, third, fourth, htons(this->clientConn.udpPort));
			CGlobal::linkCCharWithUdpPort(this->clientConn.clientIP, this->clientConn.udpPort,this);
		}
	}
	CGlobal::exitCharList();
}

unsigned int CChar::getTradeID() {
	return m_tradeID;
}

void CChar::setTradeID(unsigned int newTradeID) {
	CGlobal::waitCharList();
	m_tradeID = newTradeID;
	CGlobal::exitCharList();
}

void CChar::sendTradeRequest(unsigned int targId) {
	CChar * target = getCChar(0, "", targId);
	if(target == NULL) {
		CConsole::outErr("Trading with NULL character");
		return;
	} else {
		if( (this->getTradeID() == 0) && (target->getTradeID() == 0) ) {
			CTradeHandler::createTrade(this, target);
		} else if(this->getTradeID() > 0) {
			CConsole::outDebOnly("Trade request from character already in trade");
		} else {
			CConsole::outDebOnly("Target character already in trade");
			this->quePacket(CTradeActionPacket(target, 0x01));
		}
	}
}

void CChar::sendTradeAction(unsigned long action) {
	CTradeHandler::tradeAction(this, action);
}

void CChar::sendTradeItem(unsigned short itemID, unsigned long quantity, unsigned char invSlotID, unsigned char tradeSlotID) {
	if((tradeSlotID > 0) && (invSlotID == 0) ) {
		CTradeHandler::removeItemTrade(tradeSlotID, this);
		CConsole::outDebOnly("Removing item from trade slot: %i", tradeSlotID);
		return;
	}

	unsigned int ownedQuantity;

	if (invSlotID == 0) //Gil
	{
		ownedQuantity = this->getGil();
	}
	else
	{
		CItem * invItem = NULL;
		invItem = getItem(LOC_INVENTORY, invSlotID);

		if(invItem == NULL) 
		{
			CConsole::outDebOnly("Requested item is not in inventory slot %d", invSlotID);
			return;
		}
		else
		{
			ownedQuantity = invItem->getQuantity();
		}
	}
	
	if(quantity <= ownedQuantity) 
	{
		CTradeHandler::addItemTrade(itemID, quantity, invSlotID, tradeSlotID, this);
	} 
	else 
	{
		CConsole::outDebOnly("Requested quantity is more than inventory quantity");
		return;
	}
}

void CChar::sendTradeNPC(CTradeContainer * tradeItems) {
	if(this->m_pTarget->getObjectType() == OBJ_NPC)
		CLuaManager::OnTrade(this,this->m_pTarget,tradeItems);
}

void CChar::setShop(unsigned char * shop) {
	memset(&m_shop, 0, 500);
	memcpy(&m_shop, shop, sizeof(m_shop));
}

void CChar::setShopNation(unsigned char nation) {
	m_shopNation = nation;
}

unsigned char CChar::getShopNation() {
	return m_shopNation;
}

void CChar::buyShop(unsigned char shopSlotID, unsigned long quantity) {
	unsigned long price;
	memcpy(&price, m_shop+(0x08 * shopSlotID)+0x04, 4);
	unsigned short itemID;
	memcpy(&itemID, m_shop+(0x08 * shopSlotID)+0x08, 2);
	unsigned char shopSlot;
	memcpy(&shopSlot, m_shop+(0x08 * shopSlotID)+0x0A, 1);
	if( (price * quantity) > getGil()) {
		CConsole::outDebOnly("Character does not have enough gil to purchase items.");
		return;
	}
	unsigned char invSlotID = 0x00;
	invSlotID = addItem(itemID, LOC_INVENTORY, quantity);
	if(invSlotID != 0xFF) {
		removeGil(price*quantity);
		quePacket(CDownloadingDataPacket());
		quePacket(CShopBuyPacket(shopSlotID, quantity));
		for(int i=0; i<16; i++) 
	{
		this->quePacket(CEquipPacket(equip[i],i));
		CItem * item = NULL;
		item = getItem(LOC_INVENTORY, equip[i]);
		if(item != NULL) {
			unsigned short itemID = item->getId();
			unsigned long quantity = item->getQuantity();
			this->quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, equip[i], INV_NODROP));
		}
	}
		sendFinishInvStor();
		
	} else
		CConsole::outDebOnly("Character's inventory is full.");
}

void CChar::appraiseShop(unsigned char invSlotID, unsigned short itemID, unsigned long quantity) {
	CItem * item = NULL;
	item = getItem(LOC_INVENTORY, invSlotID);
	if(item == NULL)
		return;
	if(item->getId() != itemID)
		return;
	unsigned char fameLevel = 0;
	fameLevel = getFameLevel(getShopNation());
	unsigned long sellPrice = 0;
	sellPrice = item->getSellPrice();

	m_shopInvSlot = invSlotID;
	m_shopQuantity = quantity;

	quePacket(CShopValuePacket(invSlotID, sellPrice, fameLevel));
	CConsole::outDebOnly("Appraising Inv slot: %i, ItemID: %i",invSlotID, itemID);
}

void CChar::sellShop() {
	CItem * item = NULL;
	item = getItem(LOC_INVENTORY, m_shopInvSlot);
	if(item == NULL)
		return;
	// Calculate gil based on sell price and fame
	unsigned long sellPrice = 0;
	sellPrice = item->getSellPrice();
	unsigned char fameLevel = 0;
	fameLevel = getFameLevel(getShopNation());
	double fameMult = 0.00;
	fameMult = 1 + (0.025 * (fameLevel - 1) / 8);
	unsigned long price = 0;
	price = (int)ceil(sellPrice * fameMult);

	// Take item
	removeItem(m_shopInvSlot, LOC_INVENTORY, m_shopQuantity);
	// Add gil
	unsigned long gil = 0;
	gil = getGil();
	setGil(gil + price * m_shopQuantity);
	// Done
	sendFinishInvStor();

	CConsole::outDebOnly("Selling quantity %i of item in inv slot %i",m_shopQuantity,m_shopInvSlot);
}

void CChar::sendGuildBuyMenu() {
	unsigned short guildID;
	memcpy(&guildID, &m_shop, 2);
	CConsole::outDebOnly("Char opens buy menu for guild shop %i",guildID);
	quePacket(CGuildBuyMenuPacket(guildID, this));
}

void CChar::sendGuildSellMenu() {
	unsigned short guildID;
	memcpy(&guildID, &m_shop, 2);
	CConsole::outDebOnly("Char opens sell menu for guild shop %i",guildID);
	quePacket(CGuildSellMenuPacket(guildID, this));
}

void CChar::sendGuildBuy(unsigned short itemID, unsigned char quantity) {
	unsigned short guildID;
	memcpy(&guildID, &m_shop, 2);
	unsigned char invSlotID = 0x00;
	invSlotID = addItem(itemID, LOC_INVENTORY, quantity);
	if(invSlotID != 0xFF) {
		unsigned char newGuildQty = 0;
		newGuildQty = CGuildShopHandler::removeItemQuantity(guildID, itemID, quantity);
		quePacket(CGuildBuyMessagePacket(itemID, newGuildQty, quantity));
		unsigned long price = 0;
		price = CGuildShopHandler::getItemPrice(guildID, itemID);
		removeGil(price);
		sendFinishInvStor();
		CConsole::outDebOnly("Char buys %i of item %i from guild shop %i",quantity, itemID, guildID);
	} else {
		unsigned char guildQty = 0;
		guildQty = CGuildShopHandler::getItemQuantity(guildID, itemID);
		quePacket(CGuildBuyMessagePacket(itemID, guildQty, 0));
		CConsole::outDebOnly("Char inventory full.  Cannot buy from guild.");
	}
}

void CChar::sendGuildSell(unsigned short itemID, unsigned char invSlotID, unsigned char quantity) {
	unsigned short guildID;
	memcpy(&guildID, &m_shop, 2);
	unsigned char newGuildQty;
	newGuildQty = CGuildShopHandler::addItemQuantity(guildID, itemID, quantity);
	if(newGuildQty != 0xFF) {
		quePacket(CGuildSellMessagePacket(itemID, newGuildQty, quantity));
		quePacket(CDownloadingDataPacket());
		unsigned long sellPrice;
		sellPrice = CGuildShopHandler::getItemSellPrice(guildID, itemID);
		removeItem(invSlotID, LOC_INVENTORY, quantity);
		unsigned long gil;
		gil = getGil();
		setGil(gil + (sellPrice * quantity));
		CConsole::outDebOnly("Char sells %i of item %i to guild shop %i",quantity, itemID, guildID);
	} else {
		unsigned char guildQty = 0;
		guildQty = CGuildShopHandler::getItemQuantity(guildID, itemID);
		quePacket(CGuildSellMessagePacket(itemID, guildQty, 0));
		CConsole::outDebOnly("Guild has too much of item %i.  Cannot sell to guild.", itemID);
	}
}

// removes the char from the global charlists, the linkshell list
void CChar::removeFromAllLists() 
{
	//add new lists here
}

// returns a pointer to a char matching a given charid or charname
CChar * CChar::getCChar(int charid, char charname[16], int targid) {
	int i = 0;
	CChar * resultChar=NULL;
	if (charid!=0)
		resultChar = CGlobal::getCCharById(charid);
	else if (strlen(charname)>0)
		resultChar = CGlobal::getCCharByName(charname);
	else if (targid>0)
		resultChar = CGlobal::getCCharByTargId(targid);

	return resultChar;
}

void CChar::setTargId(unsigned int targid)
{
	unsigned int oldTargId = this->getTargId();
	if (oldTargId!=0)
		CGlobal::unlinkCCharWithTargId(oldTargId);

	//CConsole::outDebOnly("CChar: setTargId()");
	CBaseEntity::setTargId(targid);

	if (targid!=0)
		CGlobal::linkCCharWithTargId(targid,this);
}

void CChar::setFame(unsigned char nation, unsigned short fame) {
	switch(nation) {
		case 0:
			fameSan = fame;
			break;
		case 1:
			fameBas = fame;
			break;
		case 2:
			fameWin = fame;
			break;
		case 3:
			fameNorg = fame;
			break;
	}
}

unsigned short CChar::getFame(unsigned char nation) {
	switch(nation) {
		case 0:
			return fameSan;
		case 1:
			return fameBas;
		case 2:
			return fameWin;
		case 3:
			return fameNorg;
		case 4: {  // Selb/Rab
			unsigned short average = 0;
			average = fameSan + fameBas;
			average = (unsigned short)( (float)average / 2 );
			return average;
		}
		case 5: {  // Jeu
			unsigned short average = 0;
			average = fameSan + fameBas + fameWin;
			average = (unsigned short)( (float)average / 3 );
			return average;
		}
	}
	return 0;
}

unsigned char CChar::getFameLevel(unsigned char nation) {
	unsigned short fame = 0;
	fame = getFame(nation);

	unsigned char fameLevel = 1;
	if (fame >= 2450)
		fameLevel = 9;
	else if (fame >= 2200)
		fameLevel = 8;
	else if (fame >= 1950)
		fameLevel = 7;
	else if (fame >= 1700)
		fameLevel = 6;
	else if (fame >= 1300)
		fameLevel = 5;
	else if (fame >= 900)
		fameLevel = 4;
	else if (fame >= 500)
		fameLevel = 3;
	else if (fame >= 200)
		fameLevel = 2;

	return fameLevel;
}

void CChar::sendSynth(unsigned short crystalItemID, unsigned char crystalInvSlotID) {
	
	// Get crystal animation effect
	unsigned short effect = 0x0000;
	switch(crystalItemID) {
		case 0x1000:
		case 0x108E:
			effect = EFFECT_FIRESYNTH;
			break;
		case 0x1001:
		case 0x108F:
			effect = EFFECT_ICESYNTH;
			break;
		case 0x1002:
		case 0x1090:
			effect = EFFECT_WINDSYNTH;
			break;
		case 0x1003:
		case 0x1091:
			effect = EFFECT_EARTHSYNTH;
			break;
		case 0x1004:
		case 0x1092:
			effect = EFFECT_LIGHTNINGSYNTH;
			break;
		case 0x1005:
		case 0x1093:
			effect = EFFECT_WATERSYNTH;
			break;
		case 0x1006:
		case 0x1094:
			effect = EFFECT_LIGHTSYNTH;
			break;
		case 0x1007:
		case 0x1095:
			effect = EFFECT_DARKSYNTH;
			break;
	}

	if(!isRecipe(crystalItemID)) {
		delSynth();
		quePacket(CSynthResultPacket(SYNTH_BADRECIPE));
		setAnimation(ANIM_NONE);
		sendCharUpdate();
		CConsole::outDebOnly("Recipe not found. Synth aborted.");
		return;
	} else if(!hasSynthSkill()) {
		delSynth();
		quePacket(CSynthResultPacket(SYNTH_NOSKILL));
		setAnimation(ANIM_NONE);
		sendCharUpdate();
		CConsole::outDebOnly("Not enough skill. Synth aborted.");
		return;
	} else {
		unsigned char invSlotID = 0xFF;
		CTradeContainer * synthContainer = NULL;
		synthContainer = getSynth();

		// Mark items in inventory as not selectable
		for(unsigned char slotID = 1; slotID <= 8; slotID++) {
			unsigned char tempSlotID;
			tempSlotID = synthContainer->getInvSlotID(slotID);
			if( (tempSlotID > 0) && (invSlotID != tempSlotID) ) {
				invSlotID = tempSlotID;
				unsigned short itemID = synthContainer->getItemID(slotID);
				unsigned long quantity = inventory->getQuantity(invSlotID);

				quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, invSlotID, INV_NOSELECT));
			}
		}
		// Calculates results and puts result param into m_synthResult
		unsigned char result = 0xFF;
		result = calcSynthResult();
		if(result != 0xFF) {
			insertPacket(CHAR_INRANGE_SELF, CKneelAnimationPacket(this, ANIM_SYNTH, effect, m_synthResult));
			m_synthResult = result;

			// Lock char into synth animation
			setAnimation(ANIM_SYNTH);
			sendCharUpdate();
		}
	}
	// Remove 1 crystal
	removeItem(crystalInvSlotID, LOC_INVENTORY, 1);
}

bool CChar::hasSynthSkill() {
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4096];
	int count = 0;

	memset(query,0,4096);
	sprintf(query, "SELECT Wood, Smith, Gold, Cloth, Leather, Bone, Alchemy, Cook FROM crafting WHERE ID = %i",m_synthRecipe);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		CConsole::outErr("Error looking up recipe skill values.");
		return false;
	}
	if(!mysql_num_rows(res)){
		CConsole::outErr("Recipe not found.");
		mysql_free_result(res);
		return false;
	}
	row = mysql_fetch_row(res);
	for(unsigned char skillID = 49; skillID < 57; skillID ++) {
		unsigned short skillValue = atoi(row[skillID - 49]);
		if( (s.skillId[skillID]/10) < (skillValue - 15) )
			return false;
	}
	mysql_free_result(res);
	return true;
}

bool CChar::isRecipe(unsigned short crystalItemID) {
	CTradeContainer * synthContainer = NULL;
	synthContainer = getSynth();
	if(synthContainer == NULL)
		return false;

	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4096];
	int count = 0;

	memset(query,0,4096);
	// Normal crystal
	if( (crystalItemID >= 0x1000) && (crystalItemID <= 0x1007) ) {
		sprintf(query, "SELECT `ID` FROM crafting WHERE Crystal = %i AND Ingredient1 = %i AND Ingredient2 = %i AND Ingredient3 = %i AND Ingredient4 = %i AND Ingredient5 = %i AND Ingredient6 = %i AND Ingredient7 = %i AND Ingredient8 = %i",
			crystalItemID, synthContainer->getItemID(1), synthContainer->getItemID(2), synthContainer->getItemID(3), synthContainer->getItemID(4), synthContainer->getItemID(5), synthContainer->getItemID(6), synthContainer->getItemID(7), synthContainer->getItemID(8));
	}
	// HQ crystal
	if( (crystalItemID >= 0x108E) && (crystalItemID <= 0x1095) ) {
		sprintf(query, "SELECT `ID` FROM crafting WHERE HQCrystal = %i AND Ingredient1 = %i AND Ingredient2 = %i AND Ingredient3 = %i AND Ingredient4 = %i AND Ingredient5 = %i AND Ingredient6 = %i AND Ingredient7 = %i AND Ingredient8 = %i",
			crystalItemID, synthContainer->getItemID(1), synthContainer->getItemID(2), synthContainer->getItemID(3), synthContainer->getItemID(4), synthContainer->getItemID(5), synthContainer->getItemID(6), synthContainer->getItemID(7), synthContainer->getItemID(8));
	}
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		CConsole::outErr("Error looking up recipe");
		return false;
	}
	if(!mysql_num_rows(res)){
		mysql_free_result(res);
		return false;
	}
	row = mysql_fetch_row(res);
	m_synthRecipe = (unsigned long)atoi(row[0]);
	mysql_free_result(res);
	CConsole::outDebOnly("Recipe matches ID %i.", m_synthRecipe);

	return true;
}

void CChar::sendSynthDone() {
	// Do results of synth
	doSynthResult();

	// Delete synth from memory
	delSynth();

	// Release char from synth animation
	setAnimation(ANIM_NONE);
	sendCharUpdate();
}

void CChar::setSynth(CTradeContainer * synthContainer) {
	delSynth();
	m_synthContainer = synthContainer;
}

CTradeContainer * CChar::getSynth() {
	return m_synthContainer;
}

void CChar::delSynth() {
	if(m_synthContainer != NULL)
		delete m_synthContainer;
	m_synthContainer = NULL;
	m_synthRecipe = 0;
}

unsigned char CChar::calcSynthResult() {
	unsigned char result = 0xFF;
	srand(clock());

	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4096];
	int count = 0;

	memset(query,0,4096);
	sprintf(query, "SELECT Wood, Smith, Gold, Cloth, Leather, Bone, Alchemy, Cook FROM crafting WHERE ID = %i",m_synthRecipe);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		CConsole::outErr("Error looking up recipe skill values.");
		return result;
	}
	if(!mysql_num_rows(res)){
		CConsole::outErr("Recipe not found.");
		mysql_free_result(res);
		return result;
	}
	row = mysql_fetch_row(res);
	for(unsigned char skillID = 49; skillID < 57; skillID ++) {
		unsigned char thisResult;
		unsigned char checkSkill = (unsigned char)atoi(row[skillID - 49]);
		if(checkSkill != 0) {
			unsigned char hqtier = 0;
			double success = 0;

			unsigned char currentSkill = 0;
			currentSkill = (unsigned char)(getSkill(skillID)/10);
			if(currentSkill >= checkSkill) {
				success = 0.95;

				char skillDiff = 0;
				skillDiff = currentSkill - checkSkill;
				if( (skillDiff >= 0) && (skillDiff <=10) )
					hqtier = 1;
				if( (skillDiff >= 11) && (skillDiff <=30) )
					hqtier = 2;
				if( (skillDiff >= 31) && (skillDiff <=50) )
					hqtier = 3;
				if( (skillDiff >= 51) && (skillDiff <=70) )
					hqtier = 4;
				if(skillDiff >= 71)
					hqtier = 5;
			} else {
				char skillDiff = 0;
				skillDiff = checkSkill - currentSkill;
				success = 0.95 - ((float)skillDiff / 10);
				if(success < 0.05)
					success = 0.05;
			}

			srand(clock());
			double random = rand() / ((double) RAND_MAX);
			CConsole::outDebOnly("Success: %g  Random: %g", success, random);
			if(random < success) {
				thisResult = 0;
				// Check for HQ
				for(unsigned char i = 0; i < 3; i ++) {
					random = rand() / ((double) RAND_MAX);
					CConsole::outDebOnly("HQ Tier: %i  Random: %g", hqtier, random);
					switch(hqtier) {
						case 5:
							if(random < 0.70)
								thisResult += 1;
							else
								i = 3;
							break;
						case 4:
							if(random < 0.50)
								thisResult += 1;
							else
								i = 3;
							break;
						case 3:
							if(random < 0.30)
								thisResult += 1;
							else
								i = 3;
							break;
						case 2:
							if(random < 0.10)
								thisResult += 1;
							else
								i = 3;
							break;
						case 1:
							if(random < 0.015)
								thisResult += 1;
							else
								i = 3;
							break;
						default:
							i = 3;
					}
					hqtier -= 1;
				}
				if(thisResult > 0)
					thisResult++;
				if(thisResult < result)
					result = thisResult;
			} else {
				result = 1;
				break;
			}
		}
	}
	mysql_free_result(res);
	switch(result) {
		case RESULT_SUCCESS:
			m_synthResult = RESULT_SUCCESS;
			CConsole::outDebOnly("Synth success.");
			break;
		case RESULT_FAIL:
			m_synthResult = RESULT_FAIL;
			CConsole::outDebOnly("Synth failed.");
			break;
		case RESULT_HQ:
			m_synthResult = RESULT_HQ;
			CConsole::outDebOnly("Synth HQ.");
			break;
		case RESULT_HQ2:
			m_synthResult = RESULT_HQ;
			CConsole::outDebOnly("Synth HQ2.");
			break;
		case RESULT_HQ3:
			m_synthResult = RESULT_HQ;
			CConsole::outDebOnly("Synth HQ3.");
			break;
	}
	return result;
}

void CChar::doSynthResult() {
	CTradeContainer * synthContainer = NULL;
	synthContainer = getSynth();
	if(synthContainer == NULL)
		return;

	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4096];
	int count = 0;

	memset(query, 0, sizeof(query));

	switch(m_synthResult) {
		case RESULT_FAIL:
			doSynthFail();
			// 50% chance of normal chance to do a skillup
			if(rand()%2)
				doSynthSkillUp();
			quePacket(CSynthResultPacket(synthContainer, SYNTH_FAIL));
			insertPacket(CHAR_INRANGE, CSynthResultShowPacket(synthContainer, SYNTH_FAIL, this));
			return;
		case RESULT_SUCCESS:
			sprintf(query, "SELECT `Result`, `ResultQty` FROM crafting WHERE ID = %i",m_synthRecipe);
			break;
		case RESULT_HQ:
			sprintf(query, "SELECT `ResultHQ1`, `ResultHQ1Qty` FROM crafting WHERE ID = %i",m_synthRecipe);
			break;
		case RESULT_HQ2:
			sprintf(query, "SELECT `ResultHQ2`, `ResultHQ2Qty` FROM crafting WHERE ID = %i",m_synthRecipe);
			break;
		case RESULT_HQ3:
			sprintf(query, "SELECT `ResultHQ3`, `ResultHQ3Qty` FROM crafting WHERE ID = %i",m_synthRecipe);
			break;
	}

	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		CConsole::outErr("Error looking up synth result");
		return;
	}
	row = mysql_fetch_row(res);
	unsigned short itemID = (unsigned short)atoi(row[0]);
	unsigned char quantity = (unsigned char)atoi(row[1]);
	mysql_free_result(res);
	quePacket(CSynthResultPacket(itemID, quantity, SYNTH_SUCCESS));
	insertPacket(CHAR_INRANGE, CSynthResultShowPacket(itemID, quantity, SYNTH_SUCCESS, this));
	CConsole::outDebOnly("Synthed recipe %i.", m_synthRecipe);

	// Remove items from inventory
	unsigned char invSlotID = 0xFF;
	unsigned char removeCount = 0;
	for(unsigned char slotID = 1; slotID <= 8; slotID++) {
		unsigned char tempSlotID;
		tempSlotID = synthContainer->getInvSlotID(slotID);
		if(invSlotID == 0xFF)
			invSlotID = tempSlotID;

		if(invSlotID != tempSlotID) {
			CConsole::outDebOnly("Removing quantity %i from inventory slot %i",removeCount,invSlotID);
			removeItem(invSlotID, LOC_INVENTORY, removeCount);
			removeCount = 0;
			invSlotID = tempSlotID;
		}
		removeCount++;
	}
	if( (removeCount > 0) && (invSlotID != 0) ) {
		CConsole::outDebOnly("Removing quantity %i from inventory slot %i",removeCount,invSlotID);
		removeItem(invSlotID, LOC_INVENTORY, removeCount);
	}

	addItem(itemID, LOC_INVENTORY, quantity);
	sendFinishInvStor();
	doSynthSkillUp();
}

void CChar::doSynthFail() {
	CTradeContainer * synthContainer = NULL;
	synthContainer = getSynth();
	if(synthContainer == NULL)
		return;

	unsigned char invSlotID = 0xFF;
	unsigned char lostCount = 0;
	for(unsigned char slotID = 1; slotID <= 8; slotID++) {
		unsigned char tempSlotID;
		tempSlotID = synthContainer->getInvSlotID(slotID);
		if(invSlotID == 0xFF)
			invSlotID = tempSlotID;

		// Check if this item is not in the same inventory slot
		if(invSlotID != tempSlotID) {
			if(lostCount > 0) {
				CConsole::outDebOnly("Removing quantity %i from inventory slot %i",lostCount,invSlotID);
				removeItem(invSlotID, LOC_INVENTORY, lostCount);
				lostCount = 0;
			} else {
				unsigned short itemID = synthContainer->getItemID(slotID-1);
				unsigned long quantity = inventory->getQuantity(invSlotID);
				quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, invSlotID, INV_NORMAL));
			}
			invSlotID = tempSlotID;
		}

		// At end of list
		if(tempSlotID == 0)
			return;

		// 50% chance to lose item
		unsigned char lostItem = (unsigned char)(rand()%2);
		if(lostItem) {
			lostCount++;
			// Set itemID to zero for SynthResultPacket
			synthContainer->setQuantity(0, slotID);
		}
		CConsole::outDebOnly("Item: %i  InvSlotID: %i  Lost: %i",synthContainer->getItemID(slotID), tempSlotID, lostItem);
	}
	if( (lostCount > 0) && (invSlotID != 0) ){
		CConsole::outDebOnly("Removing quantity %i from inventory slot %i",lostCount,invSlotID);
		removeItem(invSlotID, LOC_INVENTORY, lostCount);
	} else {
		unsigned short itemID = synthContainer->getItemID(8);
		unsigned long quantity = inventory->getQuantity(invSlotID);
		quePacket(CInventoryAssignPacket(itemID, quantity, LOC_INVENTORY, invSlotID, INV_NORMAL));
	}
}

void CChar::doSynthSkillUp() {
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4096];
	int count = 0;

	memset(query,0,4096);
	sprintf(query, "SELECT Wood, Smith, Gold, Cloth, Leather, Bone, Alchemy, Cook FROM crafting WHERE ID = %i",m_synthRecipe);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		CConsole::outErr("Error looking up recipe skill values.");
		return;
	}
	if(!mysql_num_rows(res)){
		CConsole::outErr("Recipe not found.");
		mysql_free_result(res);
		return;
	}
	row = mysql_fetch_row(res);
	for(unsigned char skillID = 49; skillID < 57; skillID ++) {
		unsigned short checkSkill = 0;
		checkSkill = atoi(row[skillID - 49]);
		if(checkSkill != 0) {
			double success = 0;

			unsigned char currentSkill = 0;
			currentSkill = (unsigned char)(getSkill(skillID)/10);
			if(currentSkill >= checkSkill) {
				success = 0.95;
			} else {
				char skillDiff = 0;
				skillDiff = checkSkill - currentSkill;
				success = 0.95 - ((float)skillDiff / 10);
				if(success < 0.05)
					success = 0.05;
			}
			doSkillUp(skillID, checkSkill);
		}
	}
	mysql_free_result(res);
	writeChar();
}

void CChar::sendNpcText(CBaseEntity * pEntity, unsigned long messageID) {
	quePacket(CNpcTextPacket(pEntity, messageID));
}

void CChar::sendSpecialMessage(unsigned short messageID, unsigned long param1, unsigned long param2, unsigned long param3, unsigned long param4, bool showName) {
	quePacket(CSpecialMessagePacket(this, messageID, param1, param2, param3, param4, showName));
}

void CChar::loadQuestMissionLog() {
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4096];
	int count = 0;

	memset(query,0,4096);
	sprintf(query, "SELECT logID, questMissionID, status FROM char_logs WHERE charID = %i", this->m_id);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		mysql_free_result(res);
		CConsole::outErr("Error looking up char_logs.");
		return;
	} 
	if(!mysql_num_rows(res)){
		mysql_free_result(res);
		return;
	}
	while(row = mysql_fetch_row(res)) {
		unsigned char logID = atoi(row[0]);
		unsigned long questMissionID = atoi(row[1]);
		unsigned char status = atoi(row[2]);
		if(logID <= 0x07) {
			if(status == 0x01)
				m_questLogs[logID].current[questMissionID] = true;
			if(status == 0x02)
				m_questLogs[logID].complete[questMissionID] = true;
		} else if(logID <= 0x0F) {
			if(status == 0x01)
				m_missionLogs[logID-10].current = questMissionID;
			if(status == 0x02)
				m_missionLogs[logID-10].complete[questMissionID] = true;
		} else if(logID == MISSION_COP) {
			m_copCurrent = questMissionID;
		} else if(logID == MISSION_ASSAULT) {
			if(status == 0x01)
				m_assaultLog.current = questMissionID;
			if(status == 0x02)
				m_assaultLog.complete[questMissionID] = true;
		} else if(logID == MISSION_CAMPAIGN) {
			if(status == 0x01)
				m_campaignLog.current = questMissionID;
			if(status == 0x02)
				m_campaignLog.complete[questMissionID] = true;
		} else if(logID == MISSION_ACP) {
			m_acpCurrent = questMissionID;
		}
	}
}

void CChar::setQuestMissionLogStatus(unsigned char logID, unsigned long questMissionID, unsigned char status) {
	if(status > 2)
		return;
	if(logID <= 0x07) {
		if(questMissionID >= 256) {
			CConsole::outDebOnly("questMissionID %i invalid for quests.", questMissionID);
			return;
		}
		if(status == 0x01) {
			m_questLogs[logID].current[questMissionID] = true;
			m_questLogs[logID].complete[questMissionID] = false;
		} else if(status == 0x02) {
			m_questLogs[logID].current[questMissionID] = false;
			m_questLogs[logID].complete[questMissionID] = true;
		} else {
			m_questLogs[logID].current[questMissionID] = false;
			m_questLogs[logID].complete[questMissionID] = false;
		}
	} else if(logID <= 0x0F) {
		if(questMissionID >= 64) {
			CConsole::outDebOnly("questMissionID %i invalid for Bas/San/Win/Zil.", questMissionID);
			return;
		}
		if(status == 0x01) {
			if(m_missionLogs[logID-10].current != 0xFFFFFFFF)
				setQuestMissionLogStatus(logID, m_missionLogs[logID-10].current, 0x00);
			m_missionLogs[logID-10].current = questMissionID;
			m_missionLogs[logID-10].complete[questMissionID] = false;
		} else if(status == 0x02) {
			if(m_missionLogs[logID-10].current == questMissionID)
				setQuestMissionLogStatus(logID, m_missionLogs[logID-10].current, 0x00);
			m_missionLogs[logID-10].complete[questMissionID] = true;
		} else {
			if(m_missionLogs[logID-10].current == questMissionID)
				m_missionLogs[logID-10].current = 0xFFFFFFFF;
			m_missionLogs[logID-10].complete[questMissionID] = false;
		}
	} else if(logID == MISSION_COP) {
		if(questMissionID >= 64) {
			CConsole::outDebOnly("questMissionID %i invalid for COP.", questMissionID);
			return;
		}
		if(status == 0x01) {
			//if(m_copCurrent != 0)
			//	setQuestMissionLogStatus(logID, m_copCurrent, 0x00);
			m_copCurrent = questMissionID;
		} else if(status == 0x02) {
			//if(m_copCurrent == questMissionID)
			//	setQuestMissionLogStatus(logID, m_copCurrent, 0x00);
			m_copCurrent = questMissionID + 1;
		} else {
			m_copCurrent = 0;
		}
	} else if(logID == MISSION_ASSAULT) {
		if(questMissionID >= 128) {
			CConsole::outDebOnly("questMissionID %i invalid for assts.", questMissionID);
			return;
		}
		if(status == 0x01) {
			if(m_assaultLog.current != 0xFFFFFFFF)
				setQuestMissionLogStatus(logID, m_assaultLog.current, 0x00);
			m_assaultLog.current = questMissionID;
			m_assaultLog.complete[questMissionID] = false;
		} else if(status == 0x02) {
			if(m_assaultLog.current == questMissionID)
				setQuestMissionLogStatus(logID, m_assaultLog.current, 0x00);
			m_assaultLog.complete[questMissionID] = true;
		} else {
			if(m_assaultLog.current == questMissionID)
				m_assaultLog.current = 0xFFFFFFFF;
			m_assaultLog.complete[questMissionID] = false;
		}
	} else if(logID == MISSION_CAMPAIGN) {
		if(questMissionID >= 512) {
			CConsole::outDebOnly("questMissionID %i invalid for camp.", questMissionID);
			return;
		}
		if(status == 0x01) {
			if(m_campaignLog.current != 0xFFFFFFFF)
				setQuestMissionLogStatus(logID, m_campaignLog.current, 0x00);
			m_campaignLog.current = questMissionID;
			m_campaignLog.complete[questMissionID] = false;
		} else if(status == 0x02) {
			if(m_campaignLog.current == questMissionID)
				setQuestMissionLogStatus(logID, m_campaignLog.current, 0x00);
			m_campaignLog.complete[questMissionID] = true;
		} else {
			if(m_campaignLog.current == questMissionID)
				m_campaignLog.current = 0xFFFFFFFF;
			m_campaignLog.complete[questMissionID] = false;
		}
	} else if(logID == MISSION_ACP) {
		if(questMissionID >= 64) {
			CConsole::outDebOnly("questMissionID %i invalid for acp.", questMissionID);
			return;
		}
		if(status == 0x01) {
			if(m_acpCurrent != 0xFFFFFFFF)
				setQuestMissionLogStatus(logID, m_acpCurrent, 0x00);
			m_acpCurrent = questMissionID;
		} else if(status == 0x02) {
			if(m_acpCurrent == questMissionID)
				setQuestMissionLogStatus(logID, m_acpCurrent, 0x00);
			m_acpCurrent += 1;
		} else {
			m_acpCurrent = 0xFFFFFFFF;
		}
	} else {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return;
	}

	MYSQL_RES * res;
	char query[4096];
	int count = 0;

	memset(query,0,4096);
	sprintf(query, "SELECT status FROM char_logs WHERE charID = %i AND logID = %i AND questmissionID = %i", this->m_id, logID, questMissionID);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		mysql_free_result(res);
		CConsole::outErr("Error looking up char_logs.");
		return;
	} 

	if(!mysql_num_rows(res)){
		mysql_free_result(res);
		if(status > 0) {
			memset(query,0,4096);
			sprintf(query, "INSERT INTO char_logs (`charID`, `logID`, `questmissionID`, `status`) VALUES (%i,%i,%i,%i)", this->m_id, logID, questMissionID, status);
			count = (int) strlen(query);
			MYSQL* mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, count);
			CDb::pushConn(mysqlConn); //release conn
		}
	} else {
		mysql_free_result(res);
		memset(query,0,4096);
		if(status == 0)
			sprintf(query, "DELETE FROM char_logs WHERE charID = %i AND logID = %i AND questmissionID = %i", this->m_id, logID, questMissionID);
		else
			sprintf(query, "UPDATE char_logs SET status = %i WHERE charID = %i AND logID = %i AND questmissionID = %i", status, this->m_id, logID, questMissionID);
		count = (int) strlen(query);
		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn
	}
	if( (logID == MISSION_CAMPAIGN) && (questMissionID >= 256) )
		sendUpdateQuestMissionLog(MISSION_CAMPAIGN2, status);
	else
		sendUpdateQuestMissionLog(logID, status);
}

unsigned char CChar::getQuestMissionLogStatus(unsigned char logID, unsigned long questMissionID) {
	bool current = false;
	bool complete = false;

	if(logID <= 0x07) {
		current = m_questLogs[logID].current[questMissionID];
		complete = m_questLogs[logID].complete[questMissionID];
	} else if(logID <= 0x0F) {
		if(m_missionLogs[logID-10].current == questMissionID)
			current = true;
		complete = m_missionLogs[logID-10].complete[questMissionID];
	} else if(logID == 0x10) {
		if(m_copCurrent == questMissionID)
			current = true;
		else if(questMissionID < m_copCurrent)
			complete = true;
	} else if(logID == 0x11) {
		if(m_assaultLog.current == questMissionID)
			current = true;
		complete = m_assaultLog.complete[questMissionID];
	} else if( (logID == 0x12) || (logID == 0x13) ) {
		if(m_campaignLog.current == questMissionID)
			current = true;
		complete = m_campaignLog.complete[questMissionID];
	} else if(logID == 0x14) {
		if(m_acpCurrent == questMissionID)
			current = true;
		else if(questMissionID < m_acpCurrent)
			complete = true;
	}

	if(current)
		return 0x01;
	else if(complete)
		return 0x02;

	return 0x00;
}

void CChar::sendQuestMissionLog() {
	for(unsigned char status = 0x01; status <= 0x02; status++) {
		for(unsigned char areaID = 0x00; areaID <= 0x07; areaID++ ) {
			quePacket(CQuestMissionLogPacket(this, areaID, status));
		}
	}
	quePacket(CQuestMissionLogPacket(this, MISSION_ZILART, 0x02));
	quePacket(CQuestMissionLogPacket(this, MISSION_TOAU, 0x02));
	quePacket(CQuestMissionLogPacket(this, MISSION_CAMPAIGN, 0x02));
	quePacket(CQuestMissionLogPacket(this, MISSION_CAMPAIGN2, 0x02));
	quePacket(CQuestMissionLogPacket(this, MISSION_ZILART, 0x01));
}

void CChar::sendUpdateQuestMissionLog(unsigned char logID, unsigned char status) {
	if(status == 0x00) {
		quePacket(CQuestMissionLogPacket(this, logID, 0x01));
		quePacket(CQuestMissionLogPacket(this, logID, 0x02));
	} else if(status == 0x02) {
		quePacket(CQuestMissionLogPacket(this, logID, 0x01));
		quePacket(CQuestMissionLogPacket(this, logID, status));
	} else
		quePacket(CQuestMissionLogPacket(this, logID, status));
}

void CChar::setLevelCap(unsigned char levelCap) {
	char query[1024];
	int count = 0;
 
	memset(query,0,1024);
	sprintf(query, "UPDATE chars SET level_cap = %i WHERE charid = %i", levelCap, this->m_id);
	count = (int) strlen(query);
 
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn
 
	this->m_levelCap = levelCap;
}

unsigned char CChar::getLevelCap() {
	return this->m_levelCap;
}

bool CChar::hasTitle(unsigned short title) {
	MYSQL_RES * res;
	char query[1024];
	int count = 0;

	memset(query,0,1024);
	sprintf(query, "SELECT titleid FROM char_titles WHERE titleid = %i AND charid = %i", title, this->m_id);
	count = (int) strlen(query);
 
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
 
	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
 
	if(!res)
	{
		mysql_free_result(res);
		CConsole::outErr("Error looking up char_titles table.");
		return false;
	} 
	if(mysql_num_rows(res)) {
		mysql_free_result(res);
		return true;
	}
	mysql_free_result(res);
	return false;
}

void CChar::setTitle(unsigned short title) {
	MYSQL_RES * res;
	char query[1024];
	int count = 0;

	memset(query,0,1024);
	sprintf(query, "UPDATE chars SET title = %i WHERE charid = %i", title, this->m_id);
	count = (int) strlen(query);
 
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	m_currentTitle = title;

	if(title == 0)
		return;

	memset(query,0,1024);
	sprintf(query, "SELECT titleid FROM char_titles WHERE titleid = %i AND charid = %i", title, this->m_id);
	count = (int) strlen(query);
 
	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
 
	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
 
	if(!res)
	{
		mysql_free_result(res);
		CConsole::outErr("Error looking up char_titles table.");
		return;
	} 
	if(!mysql_num_rows(res)){
		mysql_free_result(res);
		memset(query,0,1024);
		sprintf(query, "INSERT INTO char_titles (`charID`, `titleID`) VALUES (%i,%i)", this->m_id, title);
		count = (int) strlen(query);
		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn
	} else
		mysql_free_result(res);

	this->quePacket(CCharStatsPacket(this));
}

unsigned short CChar::getCurrentTitle() {
	return m_currentTitle;
}

void CChar::spawnPet(unsigned char petID) {
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[1024];
	unsigned long count  = 0, *lengths;

	memset(query,0,1024);
	sprintf(query, "SELECT name, model FROM pet_list WHERE petID = %i", petID);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res) {
		mysql_free_result(res);
		CConsole::outDebOnly("Error looking up pet_list.");
		return;
	} 
	if(!mysql_num_rows(res)){
		CConsole::outDebOnly("PetID %i does not exist in table pet_list.",petID);
		mysql_free_result(res);
		return;
	}

	CNpc * pet = new CNpc;

	if(row = mysql_fetch_row(res)) {
		lengths = mysql_fetch_lengths(res);
		memcpy(pet->m_name, row[0], lengths[0]);
		unsigned long look = atoi(row[1]);
		look = look << 16;
		pet->look = look;
	} else
		return;

	pet->isPet(true);
	pet->isCharSpawned(true);
	pet->incombat = true;
	pet->sizeMod = 0;
	pet->moveState = 2;
	pet->type = 0x07;
	pet->m_id = 18000000 + getTargId() - 0x400;
	pet->setTargId(0x300 + getTargId());
	pet->loc.zone = this->loc.zone;
	pet->loc.p = this->loc.p;
	pet->loc.p.x = (float)(pet->getXPos() + 1);  // Offset from char slightly
	pet->targ_p = pet->getPos();
	pet->status = 0;
	pet->m_speed = 0x28;
	pet->m_speedAlt	= 0x28;
	pet->h.hp = 100;
	pet->m_maxHp = 100;
	pet->namevis = 0;
	pet->unknown = 0x99;
	m_pPet = pet;
	pet->sendUpdate();
	sendCharUpdate();
}

void CChar::unlockSubjobs() {
	if(this->m_subjobUnlocked == 1)
		//Subjobs already unlocked, do nothing
		return;

	char query[1024];
	int count = 0;
 
	memset(query,0,1024);
	sprintf(query, "UPDATE char_job SET subjob = 1 WHERE charid = %i ", this->getId());
	count = (int) strlen(query);
 
	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn
 
	this->m_subjobUnlocked = 1;
	// Bit 0 in unlocked jobs = Subjob
	this->unlockJob(0);
}

void CChar::unlockJob(unsigned char jobID) {
	this->j.unlocked |= (0x00000001 << jobID);

	this->writeChar();

	this->quePacket(CCharJobInfoPacket(this));
}

DWORD WINAPI CChar::despawnPet(LPVOID param) {
	CChar * schar = (CChar*)param;
	CNpc * pet = schar->m_pPet;
	if( (pet != NULL) && (pet->getAnimation() != 0x03) && !pet->isDead()){
		pet->h.hp = 0;
		pet->setAnimation(0x03);
		pet->sendUpdate();
		Sleep(4000);
		pet->status = 0x07;
		pet->sendUpdate();
		CGlobal::waitCharList();
		delete pet;
		schar->m_pPet = NULL;
		CGlobal::exitCharList();
		schar->sendCharUpdate();
	}
	return true;
}

void CChar::isHealing(bool healing) {
	CStatusEffectHandler * statusEffectHandler = CStatusEffectHandler::getInstance();
	if(healing) {
		statusEffectHandler->addEntity((CBaseEntity*)this);
		this->m_healingStartTime = clock();
	} else {
		if(this->m_statusEffectList.size() == 0)
			statusEffectHandler->removeEntity((CBaseEntity*)this);
		this->m_healingStartTime = 0;
	}

	m_isHealing = healing;
}

bool CChar::isHealing() {
	return m_isHealing;
}

void CChar::addStatusIcon(unsigned short statusID) {
	if(m_status[31] != 0x00FF) {
		CConsole::outDebOnly("Char has 32 statuses.  Cannot add another status.");
		return;
	}
	for(unsigned char count = 0; count < 32; count++) {
		if(m_status[count] == statusID) {
			CConsole::outDebOnly("Char already has status %i.",statusID);
			return;
		}
		if(m_status[count] > statusID) {
			for(unsigned char i = 30; i > count; i--)
				m_status[i] = m_status[i-1];
			m_status[count] = statusID;
			this->quePacket(CMessagePacket(this, this, statusID, 0, 203));
			sendCharUpdate();
			return;
		}
		if(m_status[count] == 0x00FF) {
			m_status[count] = statusID;
			this->quePacket(CMessagePacket(this, this, statusID, 0, 203));
			sendCharUpdate();
			return;
		}
	}
}

void CChar::removeStatusIcon(unsigned short statusID) {
	for(unsigned char count = 0; count < 32; count++) {
		if(m_status[count] == statusID) {
			for(unsigned char i = count; i <= 30; i++)
				m_status[i] = m_status[i+1];
			m_status[31] = 0x00FF;
			this->quePacket(CMessagePacket(this, this, statusID, 0, 204));
			sendCharUpdate();
			return;
		}
	}
	CConsole::outDebOnly("Char does not have status %i.  Cannot remove status.",statusID);
}


void CChar::checkTreasurePools(void) 
{
	CChar *currChar;
	CGlobal::waitCharList();
	time_t checkTime;
	time(&checkTime);

	for(unsigned int x = 0; x < CGlobal::getCCharListSize(); x++)
	{
		currChar = CGlobal::getCCharAt(x);
		if (currChar != NULL) {
			if (currChar->hasTreasurePool()) {
				//No reason to check the same pool multiple times on the same interval
				if (currChar->getTreasurePool()->getLastCheckTime() < (unsigned int)checkTime) {
					currChar->getTreasurePool()->checkItems();
					currChar->getTreasurePool()->setLastCheckTime((unsigned int)checkTime);
				}
			}
		}
	}
	CGlobal::exitCharList();
}

int CChar::getBaseAccuracy(CBaseEntity * target, unsigned int weaponslot) {

	//Set variables
	int sourcelevel = this->m_lvlMain;
	int targetlevel = target->m_lvlMain;

	int sourcedex = this->stats.dex + this->stats.pDex;

	unsigned short weaponSkillType = 0;
	weaponSkillType = this->getWeaponType(weaponslot);
	unsigned short skill = 0;
	skill = (this->getSkill(weaponSkillType)/10);
	unsigned int cap = this->getMaxSkill(weaponSkillType);

	if(skill > cap) {
		skill = cap;
	}



	int accuracy = 0;

	//add skill to accuracy
	if (skill < 200) {
		accuracy += skill;
	} else {
		//skill is >= 200 so..
		int extraskill = skill - 200;
		//every 1 skill over 200 is worth 0.9 acc
		accuracy += (int)(200 + (extraskill * 0.9)); 
	}

	//find if using 2H or 1H weapon
	bool twohander = false;

	if (weaponSkillType == WEAPON_GAX ||
		weaponSkillType == WEAPON_GKT ||
		weaponSkillType == WEAPON_POL ||
		weaponSkillType == WEAPON_SYH || 
		weaponSkillType == WEAPON_STF ||
		weaponSkillType == WEAPON_GSD) {

		twohander = true;
	}

	//Applies DEX to acc
	if (twohander == true) {
		accuracy += (3/4) * sourcedex;
	}
	else if (twohander == false) {
		accuracy += (1/2) * sourcedex;
	}


	//Applies level bonus or penalty
	int levelbonus = 0;
	if (sourcelevel > targetlevel) {
		levelbonus = (sourcelevel - targetlevel) * 4;
		accuracy += levelbonus;
	} else if (targetlevel > sourcelevel) {
		levelbonus = (targetlevel - sourcelevel) * 4;
		accuracy -= levelbonus;
	}
	

	if(accuracy < 0 ) {
		accuracy = 0;
	}
	//Returns the combined accuracy.
	return accuracy ;


}


unsigned int CChar::getBaseEva() {

	unsigned int skill = 0;
	skill = (this->getSkill(SKILL_EVA)/10);
	skill = this->s.skillId[SKILL_EVA]/10;
	unsigned int cap = this->getMaxSkill(SKILL_EVA);

	if(skill > cap) {
		skill = cap;
	}

	return skill;

}

int CChar::getBaseDef() {

	if(this->m_lvlMain <= 50) {
		return	(this->stats.vit/2 + 8 + this->m_lvlMain); 
	}
	else if(this->m_lvlMain <= 60 ) {
		return	(this->stats.vit/2 + 8 + this->m_lvlMain + (this->m_lvlMain-50)); 
	}
	else if(this->m_lvlMain <= 200 ) {
		return	(this->stats.vit/2 + 8 + this->m_lvlMain + 10); 
	}
	return 0;
}


int CChar::getBaseAttack() {


	unsigned short weaponSkillType = 0;
	weaponSkillType = this->getWeaponType(SLOT_MAIN);
	unsigned short playerJob = this->m_jobMain;
	unsigned short skill = 0;
	unsigned int cap = 0;

	if (weaponSkillType == WEAPON_NONE && (
		playerJob == 1 ||
		playerJob == 2 ||
		playerJob == 6 ||
		playerJob == 13 ||
		playerJob == 18 ||
		playerJob == 19)) {
		weaponSkillType = WEAPON_H2H;
	}

	if (weaponSkillType != WEAPON_NONE) {
		skill = (this->getSkill(weaponSkillType)/10);
		cap = this->getMaxSkill(weaponSkillType);
	}

	if(skill > cap) {
		skill = cap;
	}

	//find if using 2H or 1H weapon
	bool twohander = false;

	if (weaponSkillType == WEAPON_GAX ||
		weaponSkillType == WEAPON_GKT ||
		weaponSkillType == WEAPON_POL ||
		weaponSkillType == WEAPON_SYH || 
		weaponSkillType == WEAPON_STF ||
		weaponSkillType == WEAPON_GSD) {

		twohander = true;
	}

	if(!twohander)
		return (8 + skill + ((this->stats.str + this->stats.pStr) /2));
	else
		return (8 + skill + ((3*(this->stats.str + this->stats.pStr)) /4));
}

void CChar::loseExp(int lostExp) {
	if (this->m_lvlMain < 4) {
		return;
	} 
	this->m_lostExp = lostExp;
	this->m_expCurrent -= lostExp;
	if (this->m_expCurrent < 0) {
		this->loseLevel();	//Level Down -_-;
		this->writeChar();
	}
	this->quePacket(CCharStatsPacket(this));
}


//Level Down :/
void CChar::loseLevel() {

	this->m_lvlMain -= 1;
	this->insertPacket(CHAR_INRANGE_SELF, CMessagePacketTwo(this, NULL, this->m_lvlMain, 0, 0x0b));
	CStats::calcStats(this);
	this->m_expForNextLevel = this->getExpForNextLevel();
	this->m_expCurrent = this->m_expCurrent+this->m_expForNextLevel;
	
	this->h.hp = 0;
	if (this->h.mp > this->getMaxMp()) {
		this->h.mp = this->getMaxMp();
	}

	this->updateAbilities();
	this->updateHealth();
	this->quePacket(CCharStatsPacket(this));
	this->quePacket(CCharSkillPacket(this));
	this->sendLevel();
	this->updateChar();
}


void CChar::sendRaiseMenu() {
	//this->m_hasRaise = 3;
	if (this->m_hasRaise == 0) 
		this->m_hasRaise = 1;
	this->quePacket(CRaiseTractorMenuPacket(this, 1));
}

void CChar::sendTractorMenu() {
	this->m_hasTractor = true;
	this->quePacket(CRaiseTractorMenuPacket(this, 2));
}

bool CChar::takeEquipedItem(unsigned char equipSlotID) {
	// for the purpose of removing 1 of a usable ammo/ranged/misc item out of an equipped stack
	CItem * item = NULL;
	unsigned char invSlotID = this->equip[equipSlotID];
	item = this->getItem(LOC_INVENTORY, invSlotID);
	unsigned short itemID = 0;
	unsigned long itemQty = 0;

	if (item > 0){
		itemID = item->getId();
		itemQty = item->getQuantity(); 
	} else {
		CConsole::outDebOnly("there is no item in equip slot %i", equipSlotID);
		return false;
	}

	if ( itemQty == 1 ){
		this->unequipItem(equipSlotID);
		this->quePacket(CEquipPacket(0, equipSlotID));
	}

	this->removeItem(invSlotID,LOC_INVENTORY,1);

	return true;
}

void CChar::gainTPFrom(int weaponslot, CBaseEntity * target) {

	if(weaponslot != SLOT_MAIN &&
		weaponslot != SLOT_OFF &&
		weaponslot != SLOT_RANGED &&
		weaponslot != SLOT_AMMO) {
		return;
	}

	float tpperhit = 0;

	int delay = this->getDelay(weaponslot);
			
	if (delay <= 180){
		tpperhit = (((delay-180)*1.5f)/180)+5;
	}
	else if (delay > 180 && delay <= 450){
		tpperhit = (((delay-180)*6.5f)/270)+5;
	}
	else if (delay > 450 && delay <= 480){
		tpperhit = (((delay-450)*1.5f)/30)+11.5f;
	}
	else if (delay > 480 && delay <= 530){
		tpperhit = (((delay-480)*1.5f)/50)+13;
	}
	else if (delay > 530 && delay <= 999){
		tpperhit = (((delay-530)*3.5f)/470)+14.5f;
	}
	else{
		CConsole::outErr("Error with delay. Value: %i", tpperhit);
	}
	
	if(this->h.tp + tpperhit < 300) {	
		this->h.tp += (int)tpperhit;
	} else {
		this->h.tp = 300;
	}

	if(target->h.tp + tpperhit < 300) {	
		target->h.tp += (short)(tpperhit/3);
	} else {
		target->h.tp = 300;
	}

	this->updateHealth();


}

unsigned int CChar::getNumAttacksFrom(int weaponslot) {
	unsigned int numattacks = 1;
	CWeaponItem * weapon=NULL;
	if(weaponslot != SLOT_MAIN && weaponslot != SLOT_OFF) {
		return 0;
	}

	weapon = this->getWeaponItem(weaponslot);
	if(weapon != NULL) {
		if(weapon->getId() == 17440 ) {
			if((rand()%100) <= 2) {
				numattacks = 5+rand()%3;
			} else if((rand()%100) <= 35) {
				numattacks = 3+rand()%3;
			} else {
				numattacks = 1+rand()%3;
			}
		}
		else if(weapon->getId() == 18020 ) {
			numattacks = 1+rand()%3;
		}
		else if(weapon->getId() == 17652 ) {
			numattacks = 1+rand()%2;
		}
		else if(weapon->getId() == 16555 ) {
			numattacks = 1+rand()%3;
		}
		else if(weapon->getId() == 17813 ) {
			numattacks = 1+rand()%3;
		}
		
	}
	return numattacks;

}

unsigned short CChar::getHitRate(CBaseEntity * target, unsigned int weaponslot) {

	//Getting variables
	int hitrate = 75;
	//Note: Level correction has already been applied.
	int playeracc = this->getAccuracy(target, weaponslot); 
	int targeteva = target->getEva();
	int hitratebonus = 0;
	int hitratepenalty = 0;
	int hitratemod = 0;

	//comparing acc and eva
	if (playeracc > targeteva){
		hitratebonus = (playeracc - targeteva)/2;
	}
	if (targeteva > playeracc){
		hitratepenalty = (targeteva - playeracc)/2;
	}

	//Applying modifications
	hitratemod = hitratebonus - hitratepenalty;
	hitrate += hitratemod;

	//Applying caps
	if (hitrate > 95){
		hitrate = 95;
	}
	if (hitrate < 20){
		hitrate = 20;
	}

	return hitrate;

}



DWORD WINAPI CharItemServ(LPVOID lpParam) {

	CChar * schar = (CChar*)lpParam;

	position tmpPos = schar->loc.p;
	CBaseEntity * tmpTarg = schar->getCChar(schar->m_itemTarget,"",0);

	if (tmpTarg == NULL) {
		return 0;
	}

	short slotID = schar->m_isUsingItem; //Doubles as the slotID -and- the double action check!
	CItem * item = schar->getItem(LOC_INVENTORY,slotID);
	if (item == NULL) {
		return 0;
	}
	CConsole::outDebOnly("%s using item %i.",schar->m_name,item->getId());
	
	long stamp = clock();
	int delay = item->getDelay();

	while(clock() < (stamp + (delay*1000))) { //Correct delay now.
		//Try to interrupt, etc.

		//Moving
		if (!(tmpPos.x == schar->loc.p.x || tmpPos.z == schar->loc.p.z)) {
			schar->quePacket(CMessagePacket(schar,schar,item->getId(),item->getId(),62));
			schar->m_isUsingItem = 0;
			return 0;
		}

		//Dead or some other harmful status
		if (schar->isDead()) {
			schar->setAnimation(0);
			schar->m_isUsingItem = 0;
			return 0;
		}

		//Dropped the item or sorted inventory
		if (!(item == schar->getItem(LOC_INVENTORY,slotID))) {
			schar->m_isUsingItem = 0;
			return 0;
		}

		//Nothing is going to change within 200 milliseconds
		// save the CPU from spiking to 100%
		Sleep(200);
	}

	//Scripts!
	CConsole::outDebOnly("NAME: %s.",item->getItemName());
	CLuaManager::OnUseItem(schar,tmpTarg,item);
	
	//Just incase something happened
	if (tmpTarg == NULL) {
		return 0;
	}
	//Do it!
	CActionPacket* attackPacket=new CActionPacket();

	attackPacket->setActorId(schar->m_id);
	attackPacket->setCategorie(5); 
	attackPacket->setStartAnimationCategorie(0x4);
	attackPacket->setStartAnimation(0x0);
	attackPacket->setParameter1(0x10);
	attackPacket->setUnknown(0x00000000);

	apActionEntry newEntry;
	memset(&newEntry,0,sizeof(newEntry));

	newEntry.unknownValue = 0x08;
	newEntry.targetAnimationID = item->m_animation; //Unique to each item.
	newEntry.messageType = item->m_message; //Unique to each item.
	newEntry.parameter2 = item->m_paramtwo;

	if(item->m_message == 95 || item->m_message == 96) {
		//attackPacket->setCategorie(4);
		attackPacket->setStartAnimationCategorie(0x00);
		attackPacket->setStartAnimation(0x0000);
		attackPacket->setParameter1(item->m_paramtwo);
		attackPacket->setUnknown(0x50000000);
		memset(&newEntry,0,sizeof(newEntry));
		newEntry.unknownValue=0x00;
		newEntry.targetAnimationID = item->m_animation;
		newEntry.unknownValue2=0x000;
		newEntry.parameter2=0x0a;
		newEntry.messageType = 0;
		newEntry.additionalMessages=0x00000000;
		newEntry.dynamisTimerFlag=0x0;
		schar->quePacket(CMessagePacket(schar,schar,item->m_paramtwo,item->m_paramtwo,item->m_message));
	}

	attackPacket->setTargetID(1,tmpTarg->getId());
	attackPacket->setActionEntry(1,1,&newEntry);

	attackPacket->finalizePacket();
	attackPacket->savePacket();

	CChar::insertPacketGlob(CHAR_INRANGE,*attackPacket,schar);

	//Will remove items from the scripts.
	//Try to remove item :(
	if (item->m_canRemove) {
		schar->removeItem(slotID,LOC_INVENTORY,1);
	} else {
		item->m_canRemove = true;
	}

	//Restore actions
	schar->m_isUsingItem = 0;
	
	return 1;
}
